من تجربیات خود را در طی سالهای کاری ام در ویرگول منتشر می کنم. اگر به برنامه نویسی علاقمند هستید این بلاگ را مانند خانه خود بدانید.
بررسی روش های معماری نرم افزار
مقدمه
در این مقاله به تشریح معماری های نرم افزار می پردازیم و لایه های معماری را در آنها بررسی می کنیم. تاریخچه ای از هر معماری را ارائه می دهیم و سیر تکامل معماری ها را تا رسیدن به معماری Clean بررسی می کنیم. در پایان به اختصار هر سه معماری را با هم مقایسه می کنیم.
در بیشتر وبلاگ ها، برنامه نویسان تنها بر روی یک معماری تمرکز می کنند، آن را تشریح و سپس پیاده سازی می کنند و از نظر من این کار نقص بزرگی در ارائه و حتی شناخت یک معماری است چون ابتدا باید تاریخچه از معماری را ارائه کرد و سپس سیر تکامل آنها تا رسیدن به معماری های امروزی درک کرد. هدف من از نوشتن این مقاله شفاف سازی این مسئله بود. بسیاری از پیاده سازی هایی که برای هر معماری ارائه می شود بسیار شبیه به هم بوده و می توان گفت مربوط به معماری Clean می باشند و این ناشی از عدم درک درست برنامه نویس از آن معماری است.
معماری هایی که در این مقاله بررسی می شود شامل موارد زیر است :
- معماری لایه ای (Layered)
- معماری شش ضلعی (Hexagonal)
- معماری پیازی (Onion)
- معماری تمیز (Clean)
در ادامه به شرح هر کدام می پردازیم ولی سه معماری آخر را با هم بررسی خواهیم کرد.
معماری لایه ای (Layered)
لایه بندی روش رایجی است برای جداسازی و سازماندهی کدها بر اساس نقش و مسئولیتی که دارند. بدین صورت که کدها را به واحدهایی تقسیم می کنیم و این واحد از لحاظ وظیفه ای که بر عهده دارند در یک لایه قرار می گیرند. در نهایت به این نتیجه می رسیم که هر لایه مسئولیت هایی جدا از سایر لایه ها دارد. در این معماری هر لایه با لایه های زیرین خود در ارتباط است و به صورت مستقل از لایه های بالایی کار می کند.
سیر تحولی معماری لایه ای تا قبل از سال 2003 بسیار مفصل است و در حوصله این مقاله نمی باشد ولی در آن سال آقای اریک ایوانس با انتشار کتاب Domain-Driven Design: Tackling Complexity in the Heart of Software. علاوه بر بیان بسیاری از مفاهیم کلیدی در رابطه با دامین ها ، نسخه هایی از لایه بندی یک سیستم نرم افزاری را نیز بیان کرد.
به صورت کلی هر سیستم نرم افزاری شامل 3 لایه می باشد.
- لایه Presentation
- لایه Bussiness
- لایه DataAccess
که البته این لایه ها در کتاب آقای ایوانس به صورت زیر تغییر پیدا کرد و این به دلیل مطرح شدن برنامه های تجاری ، افزایش تعداد کاربران ، مفاهیم ابری و به وجود آمدن پیچیدگی در برنامه ها و لایه های Infrarstucture بود.
البته لایه User Interface با نام لایه Presentation هم شناخته می شود. این لایه ها در تمامی معماری ها وجود دارند ولی ممکن است که این اسامی در آنها کمی متفاوت باشد یا اینکه بعضی از لایه های با هم ترکیب شده اند. در ادامه به تشریح وظیفه هر لایه و اینکه چه نوع کدی در هر لایه قرار می گیرد می پردازیم.
لایه Presentation
این لایه وظیفه تعامل با کاربران را دارد و دستورات کاربران را به لایه Application می رساند و کدهایی نظیر HTML یا فرم های ویندوزی در این لایه قرار می گیرند. البته ارتباط با سیستم های خارجی مانند API نیز در این لایه قرار می گیرند و مانند یک دروازه یا درگاه عمل می کند. برای مثال در یک برنامه تحت وب درخواست های کاربر در این لایه از کاربر گرفته می شود و پاسخ به آن در این لایه شکل دهی می شود و به کاربر نمایش داده می شود. امکاناتی که برنامه ارائه می دهد در این لایه قرار دارد.
کدهای Controller و Dto و Api و در برخی برنامه ها صفحات HTML در این لایه قرار می گیرند. البته با رشد برنامه های تجاری و تعداد کاربران آنها ، این لایه نیز پیچیدگی های خود را خواهد داشت و ممکن است که در یک Solution جدا توسعه داده شود.
لایه Application
دوست دارم این لایه را همانند رهبر ارکستر تعریف کنم و که وظیفه آن ایجاد هماهنگی بین لایه های دیگر است. نقش یک رهبر ارکستر را دارد. در برخی از سناریوها نیز که ممکن چند موجودیت در حین اجرای یک درخواست کاربر دچار تغییراتی شوند در این لایه می توان محدوده تراکنش را تعیین کرد. به عنوان مثالی دیگر ممکن است برای پاسخ به یک درخواست کاربر یک سری متوالی از دستورات صورت گیرد؛ در این لایه است که این توالی دستورات اجرا می شود.
کدهایی را در این لایه قرار دهید که نه به لایه Presentation و نه به لایه Domain تعلق دارند و وظیفه هماهنگی کننده را دارند و در ضمن حاوی هیچ گونه Bussiness Logic یا همان قوانین تجاری نیستند.
لایه Domain
این لایه در واقع لایه ای است که آقای ایوانس بیشترین مانور را در آن داده است و کلیه قوانین تجاری در آن قرار دارد. این لایه هسته سیستم و به تعبیری قلب سیستم نامیده می شود. قوانین تجاری، قوانینی هستند برای حل مسائلی که در دامنه مطرح می شوند. این لایه به هیچ یک از لایه های اصلی دیگر ارجاعی ندارد و مستقل از آنها عمل می کند.
کدهایی که در این لایه قرار دارند شامل Entities ، Events ، Aggregates ، Domain Services ، Factories ، Interfaces و Value Object ها و سایر کلاس هایی که به قوانین تجاری مربوط می شوند.
لایه Infrastructure
قابلیت های تکنیکالی سیستم مانند ارتباط با سرویس های خارجی در این لایه پیاده سازی می شوند. کدهای مربوط به بانک اطلاعاتی ، ارسال ایمیل یا SMS ، کار با فایلها ، ثبت لاگهای سیستم، فراخوانی API سیستم های خارجی و سرویس های ارسال پیام(Messaging Services) نظیر RabbitMq در این لایه پیاده سازی می شوند.
کدهایی که در این لایه قرار می گیرند که یکی از وظایف بالا را برعهده دارند. اینترفیس هایی که در لایه Domain یا Application برای دسترسی به بانک اطلاعاتی یا سایر سرویس های دیگر تعریف می شوند در این لایه پیاده سازی می شوند. به عنوان مثالی دیگر ارتباط با سرویس ارسال پیام کوتاه در این لایه پیاده سازی می شود.
در ادامه خواهید دید که هر معماری چه نوع نگرشی نسبت به لایه ها دارد و چگونه آنها را در کنار هم قرار می دهد و چه الگوی جدیدی را به دنیای نرم افزار ارائه می دهد. تاریخچه ای از هر معماری را ارائه خواهیم کرد و درنهایت به بررسی معماری را در کنارهم خواهیم پرداخت.
معماری Hexagon(Porta & Adapters)
این معماری در سال 2005 توسط Alistair Cockburn معرفی شد.
این معماری به برنامه اجازه می دهد تا بطور یکسان توسط کاربران یا برنامه های دیگر و اسکریپت های آزمایشی مورد استفاده قرار گیرد و از طرفی دیگر جدا از دستگاه و بانک اطلاعاتی در زمان اجرا توسعه داده شود یا آزمایش گردد. در ادامه هر بار که در باره معماری Hexagonal صحبت کردیم بدانید که در واقع منظور از لایه Application اشاره به دو لایه Application و Domain در کنار هم است که شامل Business Logic می باشند.
منظور بند قبلی این است که لایه Application و Domain جدا از لایه های Infrastructure و Presentation توسعه داده شود و آزمایش شود و از طرفی دیگر کاربران که از برنامه استفاده می کنند یا اسکریپت هایی که برنامه را تست می کنند به طور یکسان از لایه های Application و Domain استفاده کنند. تلاش این معماری بر دولایه بیرونی یعنی Infrastructure و Presentation است به طوری که لایه Application را از جزئیات بیرونی بدور نگه دارد. این کار را با استفاده از Port ها و Adaptor ها انجام داده است. این معماری تمرکز خود را بر لایه های بیرونی قرار داده است.
اصطلاحاتی در این معماری رایج است به طور مختصر به شرح زیر است:
ابتدا بیاید در باره دو فضا که در این معماری بسیار مهم هستند صحبت کنیم که عبارتند از :
- Primary/Driving
- Secondary/Driven
فضای Driving در واقع امکاناتی است که برنامه ارائه می کند و به هسته برنامه می گویند که چکاری را انجام دهد. همانطور که از شکل پیداست لایه Presentation در این فضا قرار می گیرد.
فضای Driven نیز امکانات و ابزارهایی است که برنامه از آنها استفاده می کند و آنها را فراخوانی می کند همانند سرویس های ارسال ایمیل و بانک اطلاعاتی و غیره و همانطور که از شکل پیداست لایه Infrastructure در این فضا قرار دارد.
حال بیاید به مفهوم Port بپردازیم. پورت چیست؟ Port ها چیزی بیشتر از Interface ها نیستند یعنی تعریف کننده مشخصاتی است که دو فضای معماری چگونه می توانند از هسته برنامه (لایه های Application و Domain) استفاده کنند و همچنین هسته برنامه (لایه های Application و Domain) چگونه از این فضاها استفاده کند. همانطور که در شکل می بینید Port ها متعلق به لایه های داخلی یعنی Bussiness Logic یا لایه Application است و باید مطابق با نیازهای آن تعریف شود.
برای مثال الگوی Repository را در نظر بگیرید که اینترفیس آن یک پورت است و پیاده سازی آن در لایه Infrastructure است که یک آداپتور است و در لایه Application ممکن است که به آن نیاز داشته باشیم.
منظور از Adaptor چیست ؟ واحدهایی از کد هستند مثل کلاس که هسته برنامه (لایه های Application و Domain) را به لایه های بیرونی متصل می کنند و می توانند پیاده سازی هایی از اینترفیس های Port باشند. آداپتورها در لایه های بیرونی برنامه قراردارند (لایه Presentation و Infrastructure ) در صورتی که در فضای Primary/Driving باشند به برنامه می گویند که چکاری انجام دهد و در صورتی که در فضای Secondary/Driven باشند این هسته برنامه است که به لایه بیرونی می گوید که چه کار را انجام دهد.
ویژگی این معماری این است که Adaptor ها تنها به یک ابزار خاص وابسته نمی شوند و حتی محدود به یک Port نیز نمی شوند و این یعنی اینکه شما می توانید از Adaptor های مختلفی استفاده کنید مثلا برای بانک اطلاعاتی از ابزارهای مختلفی استفاده کنید. ولی در طرف دیگر هسته برنامه تنها به Port یا اینترفیس وابسته است یعنی به انتزاع وابسته است و این یعنی اصل آزادی در انتخاب یا تغییر ابزارهای پیاده سازی شده با Adaptor ها که محدود به نوع خاصی نیست.
مزیت استفاده از این معماری این است که هسته برنامه کاملا از جزئیات پیاده سازی در لایه های بیرونی دور نگهدارشته می شود و می تواند به صورت مجزا توسعه و تست شود.
معماری Onion
این معماری در سال 2008 توسط Jeffrey Palermo معرفی شد. ایده اصلی آن استفاده از Domain و Domain Service در هسته برنامه بود. و به عنوان انقلابی بر معماری قبلی قرار گرفت. چون مفاهیم دنیای DDD را به هسته برنامه منتقل کرد و باعث شد که لایه های Infrastructure و Presentation بیشتر به بیرون رانده شوند. این معماری باعث شد که دو مفهوم Application Service و Domain Service را از هم جدا شوند.
این معماری هم همانند معماری قبل به برنامه این امکان را می دهد که بدون نیاز به لایه های Infrastructure و Presentation بتواند اجرا شود و این قابلیت مناسبی برای تست کردن برنامه است. این لایه تمرکز خود را بر لایه Domain برنامه قرار داد و مفاهیم دنیای DDD را بیشتر در این لایه وارد کرد.
معماری Clean
این معماری در سال 2012 توسط Robert C.Martin منتشر شد و موضوعات اصلی در این معماری همانند معماری های دیگر است. مستقل از ابزارهای پیاده سازی و قابلیت تست پذیری به صورت جدا.
در نمودار پایین تلاش شده است تا ایده های معماری جدید را در یک ایده جمع کند. در واقع این معماری به گونه ای تکمیل شده معماری های دیگر است که در آن سعی شده است تا مفاهیم جدید دنیای نرم افزار در یک معماری جمع شود.
تکامل معماری ها
تا اینجا شرح مختصری از هر معماری ارائه گردید در ادامه توضیحاتی را برای جمع بندی مقاله ارائه می دهیم.
همانطور که در توضیحات اشاره شد معماری ها برای سازماندهی کد و جداسازی مفاهیم و ابزارهای پیاده سازی از لایه بندی استفاده می کنند و واحدهای کد را در لایه های خود جای می دهند. هر معماری تمرکز خود را بر روی یک یا چند لایه متمرکز می کند و سعی دارد تا حالتی بهینه و کارآمد ارائه دهد. از لحاظ تست پذیری، توسعه پذیری هر معماری الگوهایی را ارائه می دهد.
زیر بنای همه معماری ها بر معماری Hexagonal قرار دارد که خود بر روی معماری لایه ای استوار است. این معماری تمرکز خود را به کمک Adaptor و Port به جداسازی لایه Application از لایه های بیرونی یعنی Presentation و Infrastructure گذاشته است و مانع از وابستگی لایه Application به جزئیات پیاده سازی و مکانیسم های تحویل می شود در این معماری بیشتر به لایه های خارجی توجه شده است. بعد از آن معماری Onion به معماری قبلی لایه های دیگری را با الهام از مبانی DDD اضافه کرد و تمرکز خود را در لایه های داخلی یعنی لایه های Business یا لایه Application و Domain قرار داد و البته از قواعدی که درباره لایه های خارجی معماری Hexagonal وضع شده بود پیروی کرد. شاید لایه Domain Service بیشترین مانوری باشد که این معماری از خود نشان داده است. اما درباره معماری تمیز Clean این معماری بیشتر سعی می کند تا ایده های معماری های اخیر را در یک معماری واحد جمع کند و ایده هایی که در لایه های مختلف به وجود آمده اند در این معماری جایگاهی پیدا کرده اند. مانند انواع روشها در پیاده سازی رابط کاربری و با انواع پیاده سازی در بانک های اطلاعاتی مختلف و انواع دستگاه هایی که برنامه بر روی آنها قرار می گیرد یا Design Pattern هایی که ممکن است در لایه Application استفاده شود. همه این ایده ها در معماری تمیز مکان مختص به خود را دارند. در رابطه با جهت وابستگی یا همان (Dependencies Direction) باید گفت که در همه معماری ها جهت آن به سمت داخل می باشد بدین معنا که لایه های داخلی درباره جزئیاتی که در لایه های بیرونی پیاده سازی می شود به هیچ عنوان اطلاعاتی ندارند و هنگامی که داده ای را از یک محدوده به سمت داخل عبور می دهیم برای مثال برای پاسخ دادن به یک درخواست کاربر این اطلاعات در قالبی که برای لایه های داخلی راحت تر است فرستاده می شود. در کل همه معماری ها اصل Dependency Inversion Principle را در سطح معماری خود به خوبی معرفی کرده اند. در معماری Hexagonal در واقع باید بگوییم که ما دو لایه بیشتر نداریم و آن لایه داخل Application یا Business و لایه خارج از Application. و همانطور که قبلا گفته شد این معماری بر جداسازی مفاهیم پیاده سازی و مکانیسم های تحویل از منطق تجاری برنامه بیشتر تاکید دارد. معماری Onion با تاکید بر معماری قبلی لایه Application را با تعریف های مبانی DDD متحول کرد و Application Service و Domain Service را به معماری قبلی اضافه کرد. در معماری Clean هنگامی که با معماری Onion مقایسه می کنیم اسامی کمی تغییر می کنند و Application Service به Use Case تغییر می کند و Domain Service به Entities تغییر می کند البته Entities مفهوم گسترده تری طبق گفته C.Martin دارد و آن شامل مجموعه ای از Data Structure ها و Function ها می شود. همان تعریف هایی که در DDD قبلا گفته شده بود. از لحاظ تست پذیری همه معماری ها با جداسازی لایه های Application و Domain از سایر لایه ها شرایطی را فراهم کرده اند که می توان این دو لایه را به صورت مجزا تست کرد و ابزارهای بیرونی را به سادگی mock کرد.
نتیجه گیری
هدف از این مقاله این بود که شرح اجمالی از تاریخ معماری نرم افزار و لایه بندی هر کدام ارائه گردد و اینکه در هر لایه از یک معماری چه نوع کدهایی زندگی می کنند و در پروژه ها هنگامی که قصد داشتیم کدی را به پروژه اضافه کنیم با علم و اطمینان کافی بدانیم که آن کد را به کدام لایه اضافه کنیم.
همچنین برای درک یک معماری لازم است تا تاریخچه معماری ها را بدانیم و سیر تکاملی آنها را تا رسیدن به معماری Clean به خوبی درک کنیم. عملا بررسی یک معماری ممکن است کار بیهوده و ناقصی به نظر برسد چون هر معماری با تکیه بر مبانی معماری قبلی الگوهایی را به آن اضافه کرده است. بنابراین شاید این درست به نظر نرسد که یک معماری را به تنهایی مورد بررسی قرار داد. ناگفته نماند بیشتر پیاده سازی هایی که من در گیت هاب برای پیاده سازی معماری ها در Net. مشاهده کردم یکسان بوده و اغلب برای معماری تمیز بوده است. کاربران زیادی از نمودارهایی برای توصیف یک معماری استفاده می کردند که هیچ ربطی به آن معماری نداشت و هرگز به وبلاگ سازنده معماری ارجاعی نداده بودند.
درنهایت باید بگویم که معماری تمیز در واقع بهترین گزینه برای پیاده سازی یک پروژه است چون در آن جایگاه بسیاری از Pattern های جدید در معماری مشخص شده است و این معماری بر شانه های معماری های قبلی ایستاده است و صادقانه باید بگویم که الگوی جدیدی را به معماری های قبلی خود اضافه نکرده است. شاید بهتر باشد که بگوییم معماری تمیز در واقع یک کشف است از الگوهایی که در بسیاری از لایه ها وجود داشته اند و حالا در یک معماری واحد جمع شده اند.
منابع
- https://alistair.cockburn.us
- https://jeffreypalermo.com/2008/07/the-onion-architecture-part-1
- https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html
- https://herbertograca.com/2017/07/03/the-software-architecture-chronicles
- https://dzone.com/articles/clean-architecture-is-screaming
- https://ademcatamak.medium.com/layers-in-ddd-projects-bd492aa2b8aa
مطلبی دیگر از این انتشارات
نحوه تشخیص Mimetype یک فایل بر اساس محتوای آن در Net.
مطلبی دیگر از این انتشارات
روش جدید ارسال ایمیل از طریق NET.
مطلبی دیگر از این انتشارات
استفاده از منوهای آبشاری کافیه