معماری نرمافزار نقش مهمی در توسعه، استقرار، عملیات و نگهداری سیستمهای نرمافزاری ایفا میکند. این معماری به تأمین نگهداری سیستم، قابلیت مقیاسپذیری، و استقرار کارآمد کمک میکند. مهمترین نقاط معماری نرمافزار به شرح زیر هستند:
رابرت سسیل مارتین به اهمیت معماری نرمافزار تأکید میکند و این معماری را به عنوان یک عامل اساسی در توسعه و مدیریت سیستمهای نرمافزاری معرفی میکند.
مارتین در مورد اهمیت معماری نرمافزار در پشتیبانی از موارد استفاده (Use cases) و عملیات (Operation) سیستم، و همچنین توسعه (Development) و استقرار (Deployment) بحث میکند. این معماری باید نه تنها موارد استفاده سیستم را پشتیبانی کند و رفتار مطلوب را ارتقاء دهد، بلکه نیازهای عملیاتی مانند توان عملیاتی و زمان پاسخگویی را نیز پشتیبانی کند. همچنین، معماری باید محیط توسعه را با تقسیم سیستم به اجزاء مستقل قابل توسعه فراهم کند و به آسانی استقرار سیستم را امکانپذیر کند.
او تاکید میکند که یک معماری نرمافزار خوب گزینههای مختلفی برای حل مسائل مختلف ارائه میدهد و از تعیین نیازمندیها، محدودیتهای عملیاتی، ساختار تیم، و نیازهای استقرار به طور دقیق پیشاهنگی نمیکند. این امکان را به توسعهدهندگان میدهد که با تغییرات در نیازمندیها و محیط سیستم به راحتی سازگار شوند.
مارتین به دو اصل اساسی تأکید میکند:
این اصول به تقسیم سیستم به اجزاء جداگانه که به راحتی قابل تغییر و نگهداری هستند کمک میکنند.
در نهایت، تفکیک لایهها و تفکیک نیازمندیها نیز به توسعهدهندگان امکان میدهد تا به صورت مستقل بر روی هر لایه کار کنند و تغییرات در یک لایه تأثیر چندانی بر لایههای دیگر نگذارند. این تفکیکلایهای به توسعه، تست، و نگهداری نرمافزار کمک کرده و به بهرهوری و کیفیت کد افزوده میشود.
معماری نرمافزار باید قابلیت افزودن یا حذف افزونهها را بدون تأثیر منفی بر سیستم اصلی فراهم کند و همچنین باید ورودیها و خروجیها را به عنوان رابطهای خارجی در نظر بگیرد و مدیریت آنها را به طور کارآمد انجام دهد.
همچنین، معماران نرمافزار باید از تصمیمگیری زودهنگام خودداری کنند، به ویژه زمانی که مواردی مانند چارچوبها، پایگاههای داده، سرورهای وب، کتابخانههای ابزار، تزریق وابستگی و موارد مشابه مورد بحث قرار میگیرد. تصمیمات زودهنگام باید فرعی و قابل تعویق باشند تا در آخرین لحظه اتخاذ شوند.
مثالهایی از داستانهای شرکتها نیز آورده شده که نشان میدهد تصمیمات زودهنگام چگونه میتوانند منجر به پیچیدگی و اشکالات در توسعه نرمافزار شوند. در نهایت، معماران نرمافزار باید تمرکز خود را بر ترسیم مرزهایی که جفت شدن را به حداقل میرسانند و انعطافپذیری را افزایش میدهند، قرار دهند تا سیستمها را با نیازهای متغیر در طول زمان سازگار کنند و از شکست خوردن آنها جلوگیری کنند.
معماری نرمافزار باید تعاملات رابط کاربر و تعیین مرزهای سیستم را مدیریت کند. این شامل اهمیت ارائه دهندگان و نماها به عنوان واسطه های کاربر میشود که مسئول تبدیل دادهها به قالب مناسب برای نمایش هستند.
معماری باید مرزهای سیستم را تعریف کند و به مدیریت کنترل دادهها و عبور از مرزها از یک لایه به لایه دیگر پرداخته و کامپوننتهای استقرار را با مقیاسپذیری و اطمینان بخشی طراحی کند. همچنین، معماری باید توانایی مدیریت همزمانی و توزیع درخواستها را داشته باشد.
از جنبههای مختلفی انواع مختلف مرزهای معماری برای سیستمهای نرمافزاری معرفی میشود، از جمله مونولیت، جزء استقرار، فرآیند محلی و سرویس. تصمیم در مورد نوع مناسب مرز معماری باید با توجه به نیازهای سیستم اتخاذ شود.
بنابراین، معماران نرمافزار باید توجه کافی به تعیین مرزها، تعاملات رابط کاربر و انتخاب مناسب نوع مرز معماری داشته باشند تا سیستمهایی ایجاد کنند که همچنان به تغییرات نیازهای در حال تغییر در طول زمان پاسخ دهند و از پیچیدگیهای زیاد و خطر شکست جلوگیری کنند.
سیاستها به عنوان قوانین تعریف میشوند که رفتار یک سیستم را تعیین میکنند. نویسنده تأکید میکند که سیاستها باید بر اساس روشی که تغییر میکنند، گروهبندی شوند.همچنین، تأثیر تغییرات معماری بر کد منبع را کاهش دادن و جداسازی خطمشیها از سیاستهای ورودی/خروجی را مورد توجه قرار میدهد. این تمرکز بر تعیین سطوح و سیاستها به معماران نرمافزار کمک میکند تا سیستمهایی ایجاد کنند که قویتر، سازگارتر و قابل نگهداریتر باشند.
معماری نرمافزار باید قوانین تجاری و سیاستهای سیستمی را به دو دسته تجزیه کند:
1. قوانین حیاتی کسب و کار (Critical Business Rules):
2. موارد استفاده (Use Cases):
مارتین تأکید دارد که قوانین تجاری باید از سایر موارد در سیستم مانند رابط کاربری و پایگاه داده جدا نگه داشته شوند. برای این منظور، او توصیه میکند که قوانین تجاری را به موجودیتها و موارد استفاده گروهبندی کنیم و از وارونگی وابستگی استفاده کنیم تا اطمینان حاصل شود که وابستگیها به مفاهیم سطح بالاتر اشاره دارند. این رویکرد به ایجاد سیستمهای نرمافزاری قویتر، سازگارتر و قابل نگهداریتر کمک میکند.
معماری نرمافزار باید تمرکز بر موارد استفاده داشته باشد و نه اتکا به چارچوبها. این به توسعهدهندگان اجازه میدهد که سیستمهای قابل آزمایش و قابل تست را طراحی و توسعه دهند.
معماری شش ضلعی (Hexagonal Architecture) یک الگوی طراحی نرمافزاری است که سه لایه اصلی شامل موجودیتها (Entities)، موارد استفاده (Use Cases) و آداپتورهای رابط (Interface Adapters) را دارد. این معماری به منظور ساخت سیستمهای مستقل از چارچوبها، قابل آزمایش و تغییر آسان استفاده میشود. معماری شش ضلعی بر پایه قاعده وابستگی عمل میکند که وابستگی کد منبع باید به سمت داخل و به سمت سیاستهای سطح بالاتر اشاره کند.
معماریهای مختلفی نیز وجود دارند که تمرکز خود را بر جداسازی لایهها و قواعد بیزینسی از عوامل خارجی و فریمورکها قرار دادهاند. این معماریها از جمله hexagonal، DCI و BCE هستند. این انتخابها سیستمهای قابل آزمایش و تغییر، بدون وابستگی به ابزارها و فریمورکها ایجاد میکنند.
معماری نرمافزاری باید از تمرکز بر فریمورکها و عوامل خارجی خودداری کرده و به ایجاد سیستمهای مستقل، قابل آزمایش و تغییر تمرکز کند. این انتخابها به تستپذیری و انعطاف در توسعه نرمافزار کمک میکنند.
مارتین در این بخش درباره الگوی "شیء فروتن" یا Humble Object و کاربردهای آن در نرمافزار صحبت میکند.
الگوی شیء فروتن، الگویی طراحی نرمافزاری است که برای جدا کردن رفتارهای سخت به تست (hard-to-test behaviors) از رفتارهای آسان به تست (easy-to-test behaviors) به کار میرود. این الگو به بهبود تستپذیری و نگهداری سیستم کمک میکند.
یک نمونه از الگوی Humble Object، الگوی Presenter در رابط کاربری گرافیکی (GUI) است که وظیفه جدا کردن نمایش دادهها از فرمتبندی و مدیریت دادهها را دارد. این تفکیک نگرانیها باعث میشود که تست کردن Presenter آسانتر شود.
الگوی Humble Object میتواند نیز در مرزگذاری بین سرویسها و ایجاد مرز میان سیستمهای مختلف به کار رود. این مرز میتواند توسط یک کلاس ساده پیادهسازی شود و تستپذیری و نگهداری سیستمها را بهبود ببخشد.
در کل، الگوی Humble Object ابزار مفیدی برای بهبود تستپذیری و نگهداری سیستمهای نرمافزاری است. این الگو با جدا کردن رفتارهای سخت به تست از رفتارهای آسان به تست کمک میکند و نوشتن تستها و تغییرات سیستم را آسانتر میکند.
در این فصل مارتین مفهوم مرزهای جزئی (Partial Boundaries) و نقش آنها در معماری نرمافزاری را مورد بحث قرار میدهد.
مرزهای جزئی تعریف میشوند تا در مراحل ابتدایی پروژه به مشکلات نگرانیهای مربوط به مرزهای کامل جلوگیری کنند. این مرزها، گامی در مسیر تجهیز به یک مرز کامل هستند و از هزینههای کمتری برخوردارند.
انواع مرزهای جزئی شامل:
مرزهای جزئی نقش مهمی در کاهش هزینهها و افزایش انعطاف در پروژههای نرمافزاری دارند. آنها به معماران اجازه میدهند تا در مواقعی که هزینه مرز کامل زیاد است یا میخواهند مکانی برای مرز آینده ایجاد کنند، از آنها استفاده کنند.
در این فصل، به مفهوم معماری نرمافزاری در بازی Hunt the Wumpus و نقش مرزها در این معماری پرداخته شده است. معماری این بازی از سه مؤلفه اصلی تشکیل شده است:
این معماری با جداسازی مؤلفه UI از مؤلفه قوانین بازی، امکان پشتیبانی از زبانهای مختلف در بازارهای مختلف را فراهم میکند. همچنین، این جداسازی از مؤلفههای مختلف UI به قوانین بازی اجازه میدهد تا از همان مؤلفههای قوانین بازی دوباره استفاده کنند. این معماری با جداسازی مؤلفهها و ایجاد APIهای مناسب بین آنها، وابستگیها را به درستی مدیریت میکند.
در نهایت، مفهوم مرزها در معماری نرمافزاری تأکید میکند که مرزها در جاهایی قرار داده شوند که هزینه پیادهسازی آنها کمتر از هزینه نادیده گرفتن آنها باشد. این جداسازی مرزها به معماران امکان انعطاف در طراحی و تغییر در سیستم را میدهد.
در این فصل، برای نشان دادن اهمیت کامپوننت اصلی، کامپوننت اصلی بازی Hunt the Wumpus مورد بررسی قرار میگیرد. این کامپوننت وظیفه ایجاد و مدیریت بازی را بر عهده دارد.
کامپوننت اصلی دارای وظایف مهمی میباشد که شامل ایجاد بازی و نقشه، تفسیر دستورات ورودی بازیکن، اجرای دستورات بازیکن، و پیگیری از وضعیت بازی و امتیازات ضربه بازیکن است. این کامپوننت در واقع مسئولیتهای اصلی بازی را بر عهده دارد و با کلیگری دیگر اجزا به هم چسبیده و متصل میشود.
کامپوننت اصلی به عنوان قسمتی اساسی از معماری بازی و نقش مهمی در عملکرد و اجرای بازی دارد. این کامپوننت میتواند به عنوان نقطه ورودی اصلی به برنامه عمل کند و تمام اجزا و قوانین بازی را به هم چسباند.
از این رو، کامپوننت اصلی یکی از قسمتهای حیاتی هر نرم افزاری است.
در این فصل، مارتین در مورد معماریهای مبتنی بر خدمات و توسعه مستقل خدمات بحث میکند. او تاکید دارد که تفکیک برنامه به خدمات مستقل تنها به تنهایی نمیتواند به عنوان یک معماری محسوب شود. در واقع، وابستگیها و محدودیتهای معماری تعیین میشوند و تعریف میشوند. نویسنده از مثال یک سیستم جمعآوری تاکسی استفاده میکند تا نشان دهد که توسعه و استقرار مستقل خدمات ممکن است دشوار باشد و خدمات معمولاً نمیتوانند به صورت کاملاً مستقل عمل کنند. مارتین به ترتیب نکات اصلی زیر را مطرح میکند:
این نکات، توجه به آنچه که معمولاً به عنوان تفکیک خدمات و خدمات مستقل در توسعه نرمافزار مورد نظر قرار میگیرد را به چالش میکشد. مارتین معتقد است که توسعه سیستمهای یکپارچه و مبتنی بر مؤلفه نیز میتواند یک گزینه مناسب باشد و توسعه و نگهداری آنها ممکن است آسانتر باشد. این دیدگاه تأکید میکند که معماری نه تنها تفکیک فیزیکی خدمات را تعریف میکند بلکه وابستگیها و محدودیتها نیز تعیین میشوند و این مسائل نباید نادیده گرفته شوند.
در این فصل، مارتین به بررسی اهمیت طراحی سیستمها برای آزمایش پذیری میپردازد. او توصیه میکند که تستها نیز به عنوان بخشی از سیستم در نظر گرفته شوند و باید مرزهای مشخصی داشته باشند. اهمیت جدا کردن تستها از کد تولید برای تقویت سیستمها و افزایش انعطافپذیری کد تولید ذکر میشود. به اختصار میتوان گفت:
با توجه به موارد بالا مارتین به اهمیت و مزایای توجه به طراحی سیستمها برای آزمایش پذیری و نگهداری آنها تأکید میکند.
در این فصل، مارتین به اهمیت تعریف صحیح و معماری سیستمهای نرمافزاری و firmware برای توسعه نرمافزارها در سیستمهای جاسازی شده اشاره میکند. او تأکید میکند که نیاز به جداسازی کد نرمافزاری از سختافزار و توسعه نرمافزارهایی با معماری مناسب و تعامل قوی با سختافزار داریم.
تغییر تعریف سیستمافزار: تعریف سنتی سیستمافزار (firmware) نادرست است و باید بهجای آن به کد تعبیه شده نگاه کنیم.
توسعه کمتر سیستمافزار و بیشتر نرمافزار: به جای توسعه سیستمافزار، باید بیشتر به توسعه نرمافزار تمرکز کنیم و کد نرمافزار را بهگونهای ساختاردهی کنیم که تکامل آن با تغییرات سختافزار آسان باشد.
نگهداری کد: برای تمیز نگه داشتن معماری نرمافزار تعبیهشده، میتوان از تکنیکهایی مانند ریفکتور کردن کد، جداسازی نرمافزار از سختافزار، و استفاده از APIها برای تعامل با سختافزار میشود.
مارتین به اهمیت تعبیهشده نویسی و معماری صحیح در سیستمهای نهفته تأکید میکند تا توسعه و نگهداری نرمافزارها را تسهیل کند.