معمار نرمافزار یک برنامه نویس بوده و هیچ وقت ارتباط خود با کد و کدنویسی را قطع نمیکند تا نتایج و تاثیرات تصمیمات خود در سطح معماری را در سطح پیادهسازی مشاهده کند. معماری نرمافزار ترکیب بندی، شکل دهی و ایجاد ارتباطات بین اجزاست به نحوی که توسعه، استقرار، هزینه نگهداری نرمافزار و بهرهوری توسعه دهندگان را با حداکثر کردن انتخابها و راهحلهای قابل اتخاذ برای آینده بهبود دهد. معماری باید کمک کند تا تیمهای مختلف حدالامکان بدون نیاز به دخالت در کار یک دیگر کارهای خود را انجام دهند. درست کار کردن برنامه هرچند حیاتی است اما نسبت به قابل تغییر و نگهداری بودن در اولیت پایینتری قرار میگیرد.
تیمهای کوچک توسعه (۵ نفره و کمتر) معمولا زمان گذاشتن برای معماری را در اوایل توسعه کاری بیهوده و وقت گیر میدانند زیرا در ابتدای توسعه همه چیز به راحتی و سرعت و بدون مشکل به پیش میرود. تیمهای بزرگ توسعه (مثلا ۵ تیم ۷ نفره) نیز بدون داشتن یک معماری خوب نباید کار خود را شروع کنند زیرا احتمال زیاد هر تیم متناسب با کامپوننت تحت توسعهی خود یک معماری را انتخاب کرده و نهایتا منجر به ۵ معماری مختلف غیرقابل سازگار با یکدیگر خواهد شد که توسعه و نگهداری سیستم را دشوار و پیچیده میکند.
هرچند استفاده از معماری میکرو سرویس در ابتدا یک تصمیم معماری مناسب به نظر میرسد اما با گذشت زمان رفته رفته به دلیل نیاز به ایجاد همکاری و ارتباط بین سرویسها و مدیریت آنها و خطاها این تصمیم چالش برانگیز خواهد شد. عملیات را میتوان هم کارایی در سختافزار در نظر گرفت که در صورت مناسب بودن معماری با افزایش منابع مشکلات آن را برطرف کرد و هم فهم کاری که نرمافزار انجام میدهد.
نگهداری یک سیستم نرمافزاری بیشترین هزینه را دارد که شامل دو بخش غارشناسی (بررسی نرمافزار موجود برای کشف باگها یا افزودن ویژگی جدید) و ریسک (افزایش ریسک احتمال اضافه کردن باگ در زمان تصحیح باگ قبلی یا اضافه کردن ویژگی جدید با پیچیدهتر شدن نرمافزار) است. در یک سطح بالا، یک سیستم نرمافزاری را میتوان به دو بخش سیاستها (قوانین کسبوکار و فرآیندها) و جزییات (مواردی که لازم است انسانها و دیگر سیستمها با سیاستها ارتباط برقرار کنند اما هرگز آن را تحت تاثیر قرار ندهند) دستهبندی نمود. بر این اساس هدف معماری نرمافزار را میتوان شکل دهی نرمافزار به نوعی دانست که سیاستها بخش اصلی آن را تشکیل داده ولی جزییات را از آن جدا کند.
برای جزییاتی که اهمیت زیادی ندارند بهتر است تصمیمی در ابتدا گرفته نشود تا به مرور زمان با آگاهی بهتری تصمیمات بهتر گرفته شده و حالات مختلف برای بهترین عملکرد امتحان شوند. مانند نوع پایگاهدادهها، زبان جستوجوی کوئری، نوع وب سرور. حتی اگر شرکتی قبلا این جزییات را مشخص کرده باشد و یک معمار خوب استخدام کند، او بدون توجه به این جزییات معماری را انجام داده و این کار او برنامه را مستقل از سکو و دستگاه خواهد کرد.
نرمافزار را میتوان به صورت لایههای عمودی (کاری که سیستم انجام میدهد و موارد کاربری که برای دلایل مختلف تغییر میکنند و باید از تاثیر گرفتن از هم مصون باشند) و افقی (برای اهداف عملکردی داخل کد) نیز بررسی کرد. جداسازی افقی به تیمهای مختلف اجازه میدهد تا روی بخشهای مختلف بدون تاثیر گزاری بر روی کار یکدیگر کار کنند.
دو نوع مضاعف شدن در نرمافزار وجود دارد، نوع واقعی (بد) که در آن یک کد یا منطق دقیقا در مکانهای مختلف تکرار شده و با هر نیاز به تغییر باید همهی آنها را تغییر دهیم. نوع دیگر مضاعف شدن نادرست (اتفاقی) است که دو کد به ظاهر یکسان به صورت موازی تکامل یافته و هر یک به دلایل متفاوتی تغییر خواهند کرد. این مضاعف شدن اتفاقی میتواند هم در لایههای عمودی باشد هم در لایه های افقی. جداسازی حالتها که با تغییر روش ارتباط بین کامپوننتّها رخ میدهد نیز میتواند در سه سطح منبع (کل کد در یک فایل قابل اجرا بوده و اتباط از طریق صدا زدن مستقیم توابع است)، توسعه (برخی کامپوننتها توزیع شده و با صدا زدن مستقیم تابع و یا به صورت مستقل با سوکت یا حافظهی مشترک ارتباط دارند) و سرویس (برخی کامپوننتها روی سرورهای دیگر و یا ماشینهای مجازی بوده و از طریق نتورک شبکه ارتباط دارند) رخ دهد.
کاپلینگ به چیزهایی که جزیی از منطق کسبوکار و موارد کاربری نیستند (مانند پایگاهدادهها، وب سرور و فریمورک)باعث دشوار شدن نگهداری نرمافزار میشود. پردازشهای محلی و سرویسها مرز محسوب میشوند در حالیکه نخ ها مرز محسوب نمیشوند و پردازشهای سطح بالا نباید شامل نام آدرس فیزیکی پردازشهای سطح پایین باشند بلکه پردازشهای سطح پایین باید به عنوان افزونه برای سطح بالاها عمل کنند. طبق یک تعریف ساده سطح کامپوننت حداقل تعداد گامهای لازم برای رسیدن از یک ورودی به خروجی است و در سطوح بالاتر با نرخ کمتری ولی به دلایل مهم تری تغییر میکنند در حالیکه سطوح پایینتر با نرخ بیشتری و به دلایل کماهمیت تری تغییر میکنند.
طبق یک تعریف ساده، یک قانون کسب و کار روندی است که به ذخیرهسازی یا ایجاد سود مالی منجر شود. روندهایی که حتی درصورت خودکارسازی نشدن هم به صورت دستی انجام میشوند روندهای حیاتی کسب و کار هستند و این روندها با دادههایی کار می کنند که حتی در صورت خودکارسازی نشدن هم وجود دارند و دادهای حیاتی نام دارند. موجودیت (قوانین مستقل کسب و کار)شیئی در نرمافزار است که ارائه دهندهی تجمیعی از روندهای حیاتی که روی دادههای حیاتی اعمال انجام میدهند میباشد که در زبانهای شیگرا موجودیتها میتوانند کلاس باشند و در زبانهای غیر شیگرا ساختاری که دادهها را به رفتار مرتبط سازد.
موارد کاربری (قوانین کسب و کار صریح و دقیق) قوانین کسب و کاری هستند که با تعریف روش خودکارسازی عملیاتهای سیستم باعث خلق سرمایه میشوند و در صورتی که کسب وکار دستی باشد وجود نخواهند داشت. موارد کاربری ورودی، گامهای پردازش و خروجی را مشخص میکنند (احضار موجودیتها). موجودیتها نسبت به مواردکاربری در سطح بالاتری بوده و به آنها وابستگی ندارند.
در یک معماری تمیز سطحها از بالاترین سطح به پایین بدین شکل هستند: موجودیتها(قوانین تجاری کسب و کار)، موارد کاربری(قوانین کاربردی کسب و کار)، آاپتورهای واسط کاربری مانند درگاه ها و کنترلرها و پرزنترها، فریمورکها و درایورها مانند دیتابیس و دستگاهها و ui و وب. هیچ چیزی در یک سطح نباید چیزی درمورد سطوح پایینتر بداند و به آن وابسته باشد.
دادهها که میتوانند بین این سطحها حرکت کنند باید برای بالاترین سطح مناسب باشند.
مواردی که استفاده از معماری تمیز توصیه میشود: پروژهّای بلند مدتی که باید با تکنولوژیهای جدید سازگار شوند، پروژهّایی که باید دستگاهای ورودی خروجی مختلف را ساپورت کنند، پروژههایی که توسط تیمهای مختلف توسعه مییابد، وقتی که احتمال دهیم که در آینده باید حالت decopling خود را عوض کنیم، وقتی که مسئله پیچیدگی ذاتی زیادی دارد، وقتی که احتمال تغییرات زیادی را داریم، وقتی که احتمال اشتباه کردن زیاد باشد.
مواردی که استفاده از معماری تمیز توصیه نمیشود: مواردی که سیستم فقط عملیات سادهی CRUD دارد، وقتی پیچیدگی ذاتی مسئله کم است، وقتی که پروژه همیشه توسط یک تیم کوچک توسعه داده خواهد شد، وقتی که سیستم پس از توسعه و انتشار تغییرات کمی خواهد داشت، وقتی که تیم به حد کافی مهارت نداشته یا برای استفاده از معماری تمیز به حد کافی قانع نشدهاند.
مرزبندی جزیی که به ما اجازهی کاهش پیچیگی را میدهد اما همچنان میتوان آن را محدودتر کرد سه نوع دارد:
۱- استفاده از حالت دیکاپلینگ در سطح منبع: کمترین محدودیت را دارد.
۲ – واسطهای کاربری گروهی یا پیادهسازیها
۳ – کاهش بار شناختی با پیاده سازی نما
در معماری تمیز کلاس main خارجی ترین لایه و سطح پایین ترین پلاگین است.
معماریهای سرویسگرا و میکروسرویسی به دو دلیل به تازگی محبوبیت بالایی یافتهاند: ۱ – سرویسها کاملا از یکدیگر جدا و ایزوله هستند ۲ – سرویسها اجازهی توسعه و استقرار مستقل را میدهند.
نرم افزارهای تعبیه شده مغمولا به دلیل وابستگی به سخت افزار، یک نرم افزار با طول عمر طولانی در نظر گرفته نمیشوند. سیستم عامل به دلیل درهم تنیدگی با سخت افزار، باید در صورت تغییر سخت افزار تغییر کند.
یک عرف خوب برای ساخت نرمافزار آنست که ۱-ابتدا برنامهای بسازید که کار کند، سپس ۲- آن را بهتر و قابل فهم تر کنید و سپس ۳- آن را سریع تر کنید.
در نرمافزارهای تعبیه شده گام دوم معمولا انجام نمیشود. در این نرمافزارها مدیریت حافظه به دلیل محدودیت آن، واکنش بلادرنگ، ورودیو خروجیّهای محدود، واسطهای کاربری غیر متعارف، حسگرها و ارتباط با دنیای واقعی اهمیت دارد.
پایگاه دادهها جزییات نرمافزار محسوب شده و جزیی از مجودیتها محسوب میشود. هرچند کار با پایگاهداده های رابطهای و ذخیره سازی و بازیابی دادهها از آنها راحت است اما به لحاظ معماری نرمافزار هیچ مزیت بزرگی نسبت به دیگر نوع های پایگاه داده از نظر سامان دهی دادهها ندارند.
موارد کاربری نباید به نوع ذخیره سازی دادهها وابستگی داشته باشند و ساختار جدولها نیز باید از لایهها و درگاههای ارتباطی پنهان بماند، هرچند کتابخانههایی وجود دارند که میتوانند خود جدولها را انتقال دهند اما اصلا چنین کاری توصیه نمیشود.
بسته به متمرکز بودن (صفحات رندر شده از سرور) یا توزیع شده بودن (اپ های فرانت اند) وب ، معماران سعی در جلوگیری از تاثیر این نوسان بر منطق کسب و کار دارند. وب نوعی واسط کاربری گرافیکی است و واسطهای کاربری گرافیکی جزییات هستند پس باید از تاثیر وب بر منطق کسب و کار جلوگیری کرد. در برنامههایی که بخش فرانت تبادل زیادی با سرور دارد شاید این کار سخت باشد بنابراین یه راه کارآمد در نظرگرفتن تعملات بخش برنامه و واسط کاربری گرافیکی به دو صورت زیر است:
1- تعاملات کوچک و ثابتی که مجموعه ورودی ها را ساخته و برای شروع یک موردکاربری ضروری هستند.
2- تعملات حیاتی کسب و کار که پس از رسیدن ورودیها به مواردکاربری اتفاق افتاده و نتایج پردازش آنها به به صورت خروجی به واسط گرافیکی کاربر برمیگردند.
چهارچوبها نیز در عین قدرت و کارآمدی بخشی از جزییات و نه معماری هستند و نباید به آن ها وابسته شد زیرا آنها نیز تغییر میکنند و توسعه دهندگان چهارچوبها تغییرات خود را متناسب با نیازهای شخصی برنامهی مورد استفادهی ما انجام نخواهند داد و در آینده، باید احتمال ایجاد ناسازگاری با برنامهی خود را داشته باشیم. استفاده از چهارچوبها معمولا ما را وابسته به آنها خواهد نمود و در صورتی که نسخهی جدید چهارچوب متناسب با نیازهای برنامهی ما نباشد، تغییر برنامه به نحوی که دیگر به این چهارچوبها وابسته نباشیم بسیار سخت و پیچیده خواهد بود برای همین در صورت نیاز به چهارچوبها بهتر است از آنها با پروکسی و پالاگین به منطق اصلی کسب و کار استفاده شود و نه به صورت مستقیم.
کتابخانههای زبان های برنامه نویسی مانند چهارچوبها نبوده و با اطمینان بیشتری میتوانیم به آنها تکیه کنیم.
اما جدای از معماری، افراد نیز به چند صورت میتوانند کد خود را سازماندهی کنند:
۱ – بسته بندی لایهها: جداسازی کد از جنبه های تکنیکی مانند کنتلر، ویو و مدل که نوعی لایهبندی افقی است.
مثلا در جاوا لایهها پکیجها هستند.
۲ – بستهبندی ویژگیها: تکه کردن عمودی بخشها و ویژگیها و مفاهیم که برای تغییر یک ویژگی راحتتر خواهیم بود.
۳ – پورتها و آداپتورها: تمامی موارد جز منطق و قوانین کسب و کار داخلی و سایر جزییات و زیرساختها را خارجی میدانیم.
مواردی که مستقل از پیاده سازی تکنیکی هستند مانند چهارچوبها و پایگاه داده داخلی، و چیزهای که به داخلیها وابستهاند را خارجی میدانیم.