معماری تمیز

معماری

معمار نرم افزار از بهترین برنامه نویسانی است در کنار برنامه نویسی، اعضای تیم را به سمت طراحی دعوت می‌کند. به شکل یا چهارچوبی که معماران نرم افزار به یک سیستم نرم افزاری می‌دهند، معماری گفته می‌شود؛ بطوریکه شامل تقسیم آن سیستم به اجزا، نحوه چیدمان و راه‌های ارتباط آن‌ها می‌باشد. معماری یک سیستم تاثیر بسیار کمی بر روی کارکرد آن سیستم دارد، در واقع هدف اصلی معماری ساده سازی توسعه، استقرار و نگهداری سیستم نرم افزاری می‌باشد.

یک معماری خوب باید از موارد زیر پشتیبانی کند:

  • توسعه: معماری یک سیستم باید توسعه را برای تیم توسعه دهنده آسان کند، در غیر اینصورت سیستم نرم افزاری عمر طولانی نخواهد داشت. به عنوان مثال، سیستمی متشکل از چند تیم توسعه که هرکدام شامل چند توسعه دهنده می‌باشد، باید به اجزای تعریف شده و ارتباطات معین و قابل اعتماد تقسیم شود. این موضوع برای یک تیم کوچک شامل چند توسعه دهنده شاید به عنوان یک مانع به نظر می‌رسد؛ به همین دلیل بسیاری از سیستم‌ها فاقد معماری خوب است.
  • استقرار: معماری یک سیستم باید قابل استقرار باشد؛ هرچه هزینه استقرار بیشتر باشد، سیستم کمتر مفید است. به عنوان مثال، اگر در توسعه اولیه یک سیستم براساس تصمیم توسعه دهندگان معماری میکرو سرویس انتخاب شود، اما در زمان استقرار متوجه این موضوع می‌شوند که تعداد سرویس‌های کوچک بسیار زیاد و اتصالات بین آن‌ها سخت و پر خطا شده است. از این رو، عدم توجه به استقرار منجر به معماری‌هایی با توسعه آسان و استقرار سخت می‌شود.
  • نگهداری: نگهداری پر هزینه‌ترین جنبه یک سیستم نرم افزاری می‌باشد، چراکه نرم افزار شامل حجم زیادی از نقص‌ها، اصلاحات و اضافه کردن ویژگی‌های جدید است که نیازمند منابع انسانی بسیاری است. از این رو، معماری خوب با جداسازی سیستم به اجزای مختلف می‌تواند باعث کاهش این هزینه‌ها شود.
  • عملکرد: تاثیر معماری سیستم بر روی عملکرد نسبت به توسعه، استقرار و نگهداری کمتر است، چراکه معماری‌هایی که مانع عملکرد می‌شوند به نسبت کم هزینه‌تر از معماری‌هایی هستند که مانع توسعه، استقرار یا نگهداری می‌شوند.
  • باز نگه داشتن گزینه‌ها: انعطاف پذیر بودن و امکان تغییر سریع و آسان نرم افزار با باز گذاشتن گزینه‌ها حاصل می‌شود. در واقع جزئیات نباید به سیاست‌های اصلی ارتباط داشته باشند تا تصمیم‌گیری در مورد جزئیات ممکن باشد.

عدم وابستگی

اولین دغدغه معمار این است که معماری باید از هدف سیستم پشتیبانی کند که این به معنای پشتیبانی از موارد کاربری برنامه هدف است. همانطور که پیش از این بیان شد، معماری تاثیر زیادی بر روی عملکرد برنامه ندارد، اما مهم‌ترین کاری که معماری خوب برای عملکرد برنامه انجام می‌دهد، شفاف سازی رفتار است تا هدف سیستم در سطح معماری قابل مشاهده باشد. از آنجایی که موارد کاربری در ساختار و سطح بالای سیستم قابل مشاهده است، بنابراین توسعه دهندگان نیازی به توصیف رفتار سیستم ندارند.

جداسازی لایه‌ها

براساس اصول SRP و CCP، معمار باید عناصری را که به دلایل مختلف تغییر می‌کنند جدا کرده و عناصری را که به دلایل مشابه تغییر می‌کنند، باتوجه به هدف سیستم جمع آوری کند. از این رو، سیستم‌ها را براساس مواردی که به طور جدا تغییر می‌کنند، به لایه‌های افقی تقسیم می‌کنیم. در عین حال، موارد کاربری برش‌های عمودی هستند که این لایه‌های افقی را تقسیم می‌کنند. در نتیجه، با استفاده از این شیوه جداسازی، می‌توان موارد جدید را بدون تداخل با موارد قبلی به سیستم اضافه کرد. همچنین این روش باعث می‌شود مواردی که توان عملیاتی بالایی دارند، پیش از مواردی که توان عملیاتی پایینی دارند، اجرا شوند. علاوه بر موارد مذکور، این روش باعث کاهش تداخل تیم‌ها، امکان توسعه مستقل و سبب انعطاف پذیری در استقرار می‌شود. باید به این نکته توجه داشت که جداسازی یک سیستم با گذشت زمان تغییر کرده و معمار باید این تغییرات را تسهیل کند.

حالت‌های جداسازی را می‌توان در سه سطح تقسیم بندی کرد:

  • سطح منبع: در این سطح وابستگی بین ماژول‌های کد منبع به صورتی است که تغییر در یک ماژول باعث تغییر یا کامپایل مجدد ماژول‌های دیگر نمی‌شود. در این حالت همه اجزاء در فضای آدرس یکسان اجرا می‌شوند و از طریق فراخوانی توابع ارتباط برقرار می‌کنند.
  • سطح استقرار: در این سطح وابستگی بین قسمت‌های قابل استقرار به صورتی است که تغییر در کد منبع یک ماژول باعث استقرار مجدد دیگران نشود. بسیاری از اجزاء در فضای آدرس یکسان هستند اما سایر اجزاء در فرآیندهای دیگر در همان پردازنده بوده و از طریق ارتباطات بین فرآیندی، سوکت‌ها یا حافظه مشترک ارتباط برقرار می‌کنند.
  • سطح سرویس: در این سطح وابستگی به صورتی است که هر واحد اجرایی مستقل از تغییرات دیگران است و ارتباط فقط از طریق بسته‌های شبکه انجام می‌شود.

مرزها

مرزها برای جداسازی عناصر نرم افزار به کار می‌روند، به طوریکه یک طرف درمورد طرف دیگر نباید اطلاعی داشته باشد. در یک معماری خوب بین چیزهایی که اهمیت دارند و چیزهایی که اهمیت ندارند یک مرز یا خط وجود دارد تا موارد فرعی قابل تعویض باشد.

به عنوان مثال در تصویر زیر، رابط کاربری گرافیکی و پایگاه داد برای قوانین تجاری اهمیت ندارد، بنابراین بین آن‌ها یک خط وجود دارد. جهت خط نیز بسیار اهمیت دارد به این معنا که پایگاه داده و رابط کاربری گرافیکی برای قوانین تجاری اهمیت ندارند اما این دو بدون قوانین تجاری وجود ندارند. باتوجه به اینکه پایگاه داده و رابط کاربری گرافیکی در اینجا به صورت پلاگین درنظر گرفته شدند، بنابراین قابل تغییر و جایگزینی هستند.

عبور از مرز تابعی در یک طرف مرز است که تابعی در طرف دیگر را در زمان اجرا فراخوانی می‌کند که ایجاد آن نیازمند مدیریت وابستگی‌های کد منبع است.

شکل‌های مختلف مرزها به صورت زیر است:

  • یکپارچه: ساده‌ترین مرزهای معماری هیچ نمایش فیزیکی دقیقی ندارند. توابع و داده‌ها به صورت منظم در یک پردازنده و فضای آدرس واحد است (حالت جداسازی سطح منبع). اینکه در حالت استقرار یکپارچه، مرزها قابل مشاهده نیستند به این معنا نیست که مرزها وجود ندارند. ساده‌ترین عبور از مرز، فراخوانی تابع از سطح پایین به سطح بالا است که در این حالت وابستگی زمان اجرا و کامپایل به سمت مولفه سطح بالاتر است.
  • فرآیندهای محلی: یک مرز معماریِ قوی‌تر است که از خط فرمان یا فراخوان سیستمی ایجاد می‌شود. فرآیندهای محلی در یک یا چند پردازنده یکسان اما در فضاهای آدرس جدا اجرا می‌شوند. بیشتر فرآیندهای محلی با استفاده از سوکت‌ها یا امکانات ارتباطی سیستم عامل بایکدیگر ارتباط برقرار می‌کنند.
  • سرویس‌ها: قوی‌ترین مرز معماری، سرویس است که عموما از خط فرمان یا فراخوان سیستم ایجاد می‌شود و به موقعیت فیزیکی آن‌ها بستگی ندارد. می‌توان گفت یک سرویس، مجموعه‌ای از فرآیندهای محلی در حال تعامل است.

سیاست و سطح

سیستم‌های نرم افزاری مجموعه‌ای کامل از سیاست‌ها است که براساس آن ورودی به خروجی تبدیل می‌شود. در بیشتر سیستم‌ها، می‌توان سیاست‌ها را به عبارات کوچک‌تر تقسیم کرد. یک معمار نرم افزار خوب باید بتواند این سیاست‌ها را به صورت دقیق جدا کرده و براساس تغییراتشان، آن‌ها را در یک گراف غیر چرخه‌ای جهت‌دار، گروه بندی کند؛ بطوریکه جهت وابستگی‌ها از اجزای سطح پایین به سمت اجزای سطح بالا است. به فاصله از ورودی‌ها و خروجی‌ها، سطح می‌گویند. هرچقدر سیاست از ورودی و خروجی سیستم دورتر باشد، سطح بالاتری دارد.

قوانین کسب و کار

قوانین کسب و کار، قوانین یا دستورالعمل‌هایی هستند که فارغ از نحوه پیاده سازی، موجب صرفه جویی در هزینه‌ها می‌شوند. در سیستم‌های کامپیوتری، موجودیت یک شیء است که شامل مجموعه‌ای از قوانین تجاری مهم است. موارد کاربری از قوانینی تشکیل شده که نحوه و زمان استفاده از قوانین تجاری مهم در موجودیت‌ها را تعیین می‌کند. درواقع موارد کاربری نحوه نمایش سیستم برای کاربر را بیان نمی‌کند، بلکه قوانین برنامه که برروی تعامل کاربران و نهادها حاکم است را توصیف می‌کند.

معماری فریاد

معماری نرم افزار ساختارهایی است از موارد کاربری سیستم پشتیبانی می‌کنند، بنابراین معماری باید درمورد موارد کاربری برنامه فریاد بزند! به عنوان مثال وقتی به معماری یک کتابخانه نگاه می‌کنیم، با نگاه به قفسه‌های کتاب، میزهای مطالعه و غیره کاملا واضح است که این ساختمان کتابخانه است. معماری‌ها نباید براساس فریمورک‌ها باشند، درغیر اینصورت نمی‌توانند براساس موارد کاربری عمل کنند. معماری‌های خوب بر روی موارد کاربری متمرکز شده‌اند تا معماران بتوانند ساختارهایی را که از آن‌ها پشتیبانی می‌کنند را بدون تعهد به فریمورک‌ها، ابزارها و محیط‌ها توصیف کنند. به این ترتیب، اگر این موضوع را رعایت کنیم، می‌توانیم موارد کاربری را بدون فریمورک‌ها به صورت واحد تست کنیم.

معماری تمیز

در چند دهه گذشته ایده‌های مختلفی درمورد معماری سیستم‌ها مطرح شد که هدف مشترک آن‌ها تقسیم نرم افزار به لایه‌های مختلف برای جداسازی دغدغه‌ها است. این معماری‌ها دارای ویژگی‌هایی همچون عدم وابستگی به فریمورک‌ها، طراحی رابط کاربری و پایگاه داده بوده و بدون وابستگی قوانین کسب و کار به آن‌ها قابل تست هستند.

لایه‌های مختلف نرم افزار به صورت دایره‌های متحدالمرکز در شکل زیر نمایش داده شده است. حلقه‌های داخلی سیاست و بیرونی مکانیزم‌ها هستند، بطوریکه وابستگی‌های کد منبع به سمت داخل و سیاست‌های سطح بالاتر است. دایره‌های درونی درمورد دایره‌های بیرونی چیزی نمی‌دانند.

تعریف این لایه‌های به صورت زیر است:

  • موجودیت‌ها: لایه موجودیت‌ها شامل قوانین حیاتی کسب و کار در سطح سازمان است که می‌توانند شیء با متد یا مجموعه‌ای از ساختار داده‌ها و توابع باشند.
  • موارد کاربری: لایه موارد کاربری شامل قوانین تجاری خاص برنامه است که جریان داده‌های موجودیت‌ها را کنترل می‌کنند. تغییرات در این لایه نباید بر روی موجودیت‌ها اثر بگذارد یا اینکه تحت تاثیر لایه‌های خارجی قرار گیرد.
  • آداپتورهای رابط: این لایه شامل مجموعه‌ای از آداپتورها است که فرمت موجودیت‌ها و موارد کاربری را به فرمت مناسب برای برخی عاملیت‌های خارجی مانند پایگاه داده یا وب تبدیل می‌کنند. هیچ کدی در این لایه نباید از عاملیت‌های خارجی چیزی بداند.
  • فریمورک‌ها و درایورها: این لایه شامل فریمورک‌ها و ابزارهایی مانند پایگاه داده و وب فریمورک است.

تعداد این لایه‌ها ممکن است متغییر باشد اما قانون وابستگی همواره باید اعمال شود.


ترجمه و خلاصه فصل‌هایی از کتاب معماری تمیز از رابرت مارتین