بررسی معماری پیازی Onion Architecture در نرم افزار و طراحی دامنه محور

بررسی معماری پیازی Onion Architecture در نرم افزار و طراحی دامنه محور

در این پست از وبسایت پرووید در رابطه با بررسی معماری پیازی Onion Architecture در نرم افزار و طراحی دامنه محور صحبت خواهیم کرد.

با قسمت بعدی آموزش Domain Driven Design از وب سایت پروید در خدمت شما هستیم. اگر قسمت های قبلی این آموزش را مطالعه نکرده اید اکیداً به شما توصیه میکنم که قبل از ادامه دادن این مقاله حتما مقاله های قبلی را نیز مطالعه کنید. به منظور انجام این کار به مقدمه ای بر طراحی دامنه محور Domain Driven Design مراجعه کنید.

تا به اینجای کار کمی در رابطه با ماهیت طراحی دامنه محور و یا Domain Driven Design و اجزای تشکیل دهنده آن صحبت کرده ایم. در این قسمت در رابطه با یکی از معماری های فوق العاده Domain Driven Design به نام معماری پیازی و یا Onion Architecture صحبت خواهیم کرد. اما قبل از انجام این کار بیایید کمی مباحث مطرح شده تا به اینجای کار را مرور کنیم.

موضوع اصلی طراحی دامنه محور و یا Domain Driven Design روشی است که در آن ما طراحی نرم افزار را براساس حوزه تجاری که در آن قرار داریم پیش میبریم. در واقع محرک و یا محور (Driven) طراحی نرم افزار (Design) دامنه (Domain) است. در بسیاری از برنامه هایی که بدون توجه به Domain Driven Design ایجاد شده اند، تمام دید توسعه دهنده نرم افزار فنی است. اما در Domain Driven Design دید را از فنی بودن به تجاری بودن تغییر میدهیم. از همین جهت نیاز است که همواره در طراحی یک برنامه Domain Driven Design با Domain Expert ها و یا افراد متخصص دامنه کاری مان ارتباط نزدیک داشته باشیم تا بتوانیم برنامه را براساس نیازهای آنها و فرایند های تجاری موجود در آن دامنه پیش ببریم.

یکی دیگر از موضوعات مهم در طراحی دامنه محور مفهوم زبان فراگیر و یا Ubiquitous Language بود که با استفاده از آن میتوانیم از واژه ها و زبان یکسان در فرایند پیاده سازی و طراحی استفاده کنیم. در واقعی زبانی که یک برنامه نویس و یک متخصص دامنه به طور مشترک از آن استفاده میکنند را زبان فراگیر و یا Ubiquitous Language میگوییم. در ادامه یک دامنه پیچیده را به زیر دامنه ها و یا اصطلاحاً Sub-domain هایی میشکنیم که به آنها Bounded Context میگوییم و پس از آن مرزهای هر کدام از این Bounded Context ها و سپس Contract و یا قرارداد ارتباط پیدا کردن Bounded Context ها با یکدیگر را تعریف میکنیم. از این جهت از واژه Contract در این قسمت استفاده کردیم که این واژه نیز به عنوان یک واژه فنی در این حوزه مورد استفاده قرار میگیرد. ماهیت Contract ها در ارتباط پیدا کردن Bounded Context ها با یکدیگر شبیه Interface هایی هست که در برنامه نویسی استفاده میکنیم و هر دو قراردادهایی را تعریف میکنند. پس به این ترتیب هر Bounded Context برای Bounded Context های دیگر شبیه یک جعبه سیاه و یا اصطلاحاً Block Box به نظر می آید. به عبارت دیگر Bounded Context های مختلف برای ارتباط برقرار کردن با یکدیگر هیچ نیازی به دانستن جزئیات پیاده سازی و یا Implementation Detail های یکدیگر ندارند و صرفاً با همان Contract Interface و یا اینترفیس قراردادی که هر یک تعریف میکنند با یگدیگر ارتباط برقرار میکنند و جزئیات مربوط به یکدیگر را نمیدانند.

برخی از چالش ها و اشتباهات رایج مربوط به طراحی دامنه محور را نیز مطرح کردیم که باید در پیاده سازی و طراحی یک برنامه با روش Domain Driven Design از آنها برحذر باشیم. برای مثال یک نگرش داده متمرکز و یا Data Centric در طراحی و مدل کردن دامنه و یا Domain بسیار خطرناک است. باید تمرکز به جای پیاده سازی Entity ها و Repository ها به مفاهیم هسته ای و یا Core Concept ها معطوف کنید و به جای تمرکز کردن بر روی تراکنش های بانک اطلاعاتی، بر روی تراکنش های تجاری متمرکز شوید. تمامی چیزهایی که تا به اینجای کار گفتیم خلاصه کوتاهی از مطالبی است که در قسمت های قبلی از این آموزش مطرح کرده ایم. از این پس در رابطه با موضوع این مقاله صحبت خواهیم کرد که آن را به معماری پیازی و یا Onion Architecture اختصاص دادیم. از شما میخواهیم که با دقت تمام این قسمت را مطالعه کنید.

واژه معماری پیازی و یا Onion Architecture در ابتدا توسط جفری پالرمو (Jeffrey Palermo) مطرح شد. اگر در رابطه با معماری شش ضلعی و یا Hexagonal Architecture که با آن Port and Adapters نیز میگویند اطلاعاتی داشته باشید خواهید دانست که معماری پیازی و یا Onion Architecture براساس معماری شش ضلعی بنا نهاده شده است. معماری شش ضلعی و یا Hexagonal Architecture نیز اولین بار توسط الیستر کوبرن (Alistair Cockburn) مطرح شد.

یکی از نکات بسیار مهم در شناخت معماری پیازی این است که در زمان طراحی Domain Driven Design مجبور نیستید لزوماً از معماری پیازی استفاده کنید. علاوه بر این زمانی که قصد دارید از معماری پیازی و یا Onion Architecture استفاده کنید لزوماً مجبور نیستید که Domain Driven Design را در دستور کار قرار بدهید. به عبارت دیگر هر دوی اینها از یکدیگر مستقل هستند و شما میتوانید در استفاده از Domain Driven Design از معماری های دیگر شبیه MVC استفاده کنید. ضمناً می توانید از آموزش کامل توسعه وب اپلیکیشن با ASP.NET MVC در این رابطه استفاده کنید. اما قضیه مهم این است که معماری پیازی و یا Onion Architecture یکی از قویترین و بهترین کاندید هایی است که برای طراحی معماری یک نرم افزار مورد استفاده قرار میگیرد. حال بیاید در رابطه با این موضوع صحبت کنیم که چرا معماری پیازی و یا Onion Architecture یک کاندید خوب برای استفاده شدن در Domain Driven Design است. به عبارت دیگر چه تفاوتی بین Onion Architecture و معماری های سنتی دیگری که احتمالاً میشناسید وجود دارد. در ابتدا در رابطه با معماری های سنتی صحبت میکنیم و سپس معماری پیازی را بررسی خواهیم کرد.

تفاوت معماری پیازی با معماری های سنتی نرم افزار

در معماری های سنتی که اغلب افراد در برنامه های خود پیاده سازی میکنند اغلب لایه های مختلفی تعریف میشوند. اگر بخواهیم دقیقتر بگوییم در چنین معماری هایی اغلب سه لایه مختلف تعریف میشوند: لایه User Interface و یا UI، لایه Business Logic و یا BL و لایه Data Model. البته ممکن است لایه های مختلف دیگری در این وسط وجود داشته باشند اما از آنجایی که میخواهیم بحث معماری پیازی را هرچه زودتر آغاز کنیم، ترجیح میدهیم در رابطه با مهمترین آنها صحبت کنیم.

بررسی معماری های سنتی و سه لایه

اگر کمی برنامه نویسی وب کار کرده باشید میدانید که در معماری های سه لایه سنتی، هر لایه به لایه ای که در زیر آن قرار دارد وابسته است. علاوه بر این تمامی لایه ها به طور معمول به زیرساخت های مشترکی از قبیل فریم ورک ها و یا Utility Service ها وابسته هستند. در چنین معماری پیاده سازی Separation Of Concerns بسیار دشوار است چرا که هر لایه با لایه های دیگر Coupling زیادی دارد. احتمالاً منظورمان را از کلمه Coupling و یا درهم تندیگی را میدانید. اگر چنین نیست از بررسی مفاهیم Cohesion و Coupling در توسعه نرم افزار استفاده کنید.

در چنین معماری هر لایه به لایه زیرین خود Coupling دارد و همه لایه ها اغلب به لایه زیرساخت و یا اصطلاحاً Infrastructure نیز Coupling دارند. ممکن است بدانید که ایجاد شدن Coupling لزوماً چیز بدی نیست و برای ارتباط برقرار شدن بین اجزای تشکلیل دهنده و یا Component های مختلف یک سیستم نیاز به کمی Coupling وجود دارد اما بحث اینجا است که مقدار Coupling در چنین معماری بیش از حد است. ضمناً دقت کردید که برای ترجمه Component از اجزای تشکلیل دهنده استفاده کردیم. در واقع میتوان واژه Component را هم به صورت اجزای تشکیل دهنده و هم به صورت مولفه ترجمه کرد. در واقع Component های یک سیستم اجزای مختلفی هستند که با کنار هم قرار گرفتن آنها کل یک سیستم ساخته میشود.

حال بیاید یک مثال در رابطه با معماری ای که تا بحال بررسی کردیم را در عمل ببینیم. فرض کنید یک تیم توسعه نرم افزار برای ساختن معماری مایکروسرویس های خود تصمیم گرفته است که SpringBoot برای Infrastructure Layer و یا لایه زیرساخت استفاده کند. چندین سال بعد تیم توسعه نرم افزار ما تصمیم میگیرد که یا فریم ورک SpringBoot را با یک فریم ورک دیگر عوض کرده و یا کاملاً آن را حذف کند. اگر از معماری MVC و یا معماری های مشابه استفاده کنیم، چنین تغییری در نیازمندی، نیاز به ایجاد تغییر در لایه های دیگر غیر از لایه Infrastructure نیز میباشد چرا که هر لایه به صورت Highly Coupled به فریم ورک موجود در لایه Infrastructure مرتبط شده است. در واقع بین لایه های مختلف و لایه Infrastructure Layer یک Coupling بیشتر از حد وجود دارد و به همین ترتیب احتمال ایجاد خطا در کدهای درون Business Layer و یا لایه تجاری که تمامی کدهای Business Logic و یا Business Domain را در خود دارند بسیار بالاست.

یکی از موضوعات مهم در طراحی نرم افزار این است که همیشه در یک سیستم باید اصل MECE که سرنام Mutually Exclusive Comprehensively Exhaustive هست را در نظر بگیرید. به عبارت دیگر بر اساس این اصل بین اجزای تشکیل دهنده مختلف موجود در سیستم، باید حداقل Coupling و حداکثر Cohesive را داشته باشیم. بنابراین هرچه قدر که Coupling موجود در سیستم تان بیشتر باشد تلاش بیشتری برای تغییر ایجاد کردن در سیستم و شکستن اجزای مختلف سیستم به قسمت های کوچکتر و مستقل نیاز خواهد بود.

یکی دیگر از انواع بسیار معمول Coupling در توسعه نرم افزار Coupling موجود از لایه UI و Business Logic به سمت Data Access Layer میباشد. به این نوع از Coupling های اصطلاحا Transitive Dependencies و یا وابستگی های انتقالی میگویند. به عبارت دیگر در چنین وابستگی هایی UI برنامه بدون وجود Business Logic کار نخواهد کرد و Business Logic بدون وجود Data Access Layer کار نخواهد کرد. تغییرات موجود در Data Access Layer به طور مکرر رخ میدهند و هربار که چنین تغییری اتفاق بیافتد نیاز به ایجاد کردن تغییر در Business Layer است و همانطور که قبلا نیز گفتیم ایجاد تغییر در کدهای نوشته شده در Business Layer بسیار خطرناک است. در واقع کدهای درون Business Layer شامل قوانین تجاری و یا Business Role هایی هست که برای آن دامنه و یا Domain تعریف شده اند. این موضوع زمانی خطرناک تر میشود که سیستم و یا نرم افزار فعلی پایدار و یا Stable است و به طور فراگیر توسط کاربرهای آن کمپانی و یا دامنه مورد استفاده قرار میگیرد.

رسالت معماری پیازی و یا Onion Architecture دقیقا حل و فصل کردن چنین مشکلاتی است. در واقع با استفاده ازOnion Architecture دیگر نیاز نیست که نگران این باشید که ایجاد تغییر در یکی از لایه های باعث ایجاد تغییر در لایه های دیگر میشود چرا که Coupling خاصی بین لایه ها وجود ندارد و آنهاً لزوما به لایه Infrastructure وابستگی ندارند. تصویر زیر مثالی از Onion Architecture را نشان میدهد.

onion architecture - بررسی معماری پیازی Onion Architecture در نرم افزار و طراحی دامنه محور

بررسی Onion Architecture و یا معماری پیازی

در رابطه با معماری پیازی و یا همان Onion Architecture صحبت های زیادی میشود کرد اما مهترین قضیه این است که درک کنید که در معماری پیازی هدف کنترل در هم تندگی ها و یا همان Coupling ها میباشد. قانون اصلی در معماری پیازی این است که تمامی Coupling ها به سمت مرکز معماری منعطف شده است. برای مثال، در تصویری که در قسمت بالا مشاهده میکنید میتوان موارد زیر را ذکر کرد:

  1. لایه زیرساخت و یا همان لایهInfrastructure Layer جزئیات مربوط به Application Services و Domain Services و Domain Model را میداند.
  2. در این تصویر مشخص است که Application Services جزئیات مربوط به Domain Services و Domain Model را میداند.
  3. همچین Domain Services فقط اطلاعات مربوط به Domain Model را میداند.
  4. و نهایتا Domain Model فقط جزئیات مروبوط به خودش را میداند و از لایه های بالا تر بی اطلاع است.
  5. لایه های درونی تر نباید و نیمتوانند جزئیات مربوط به لایه های بیرونی تر را بدانند.

به همین دلیل است که لایه Domain Model جزئیات مربوط به Domain Services و Application Services و همچنین Infrastructure Layer را نمیداند و همچنین لایه Domain Services جزئیات مربوط به لایه های Application Services و Infrastructure Layer و یا همان زیر ساخت را نمیداند.

در مرکز این معماری ما Domain Model را میبینیم که نمایانگر State و همچنین Behavior مربوط به مدلی است که در حال توسعه دادن آن هستیم. منظور از State به بیان خیلی ساده همان پروپرتی ها و Behavior همان متد های درون کلاس ها هست. در اطراف لایه Domain Model لایه های دیگری قرار گرفته اند که خود آنها نیز دارای Behavior های خاص خود هستند و در تعداد آنها نیز محدودیتی وجود ندارد. به عبارت دیگر لایه های اطراف Domain Model میتوانند یک عدد، دو عدد و یا بیشتر باشند. وظیفه Application Core به هم چسپاندن و مرتبط کردن تمامی لایه ها از قبل لایه ها از قبیل Domain Model و Domain Services و Application Service است. دوباره ذکر این نکته را لازم میدانیم که بگوییم تعداد لایه ها میتواند متنوع باشد و لزوماً مجبور نیستید که سه لایه در این قسمت داشته باشید.

همانطور که پیشتر نیز گفتم در مرکز این معماری Domain Model وجود دارد که فقط به سمت خودش Coupling دارد و از لایه های بیرونی تر اطلاعی ندارد. یکی از مواردی که در Domain Model قرار میدهیم اینترفیس های مربوط به Repository هایمان میباشد که اصطلاحاً به آنها Repository Interfaces نیز میگویند. این اینترفیس ها مکانیزم های ذخیره کردن و بازیابی Object ها را فراهم میکنند و به عبارت دیگر میتوان گفت که Domain Model وظیفه تغییر کردن Contract و یا قراردادی را دارد که طبق آن Object های موجود در سیستم مورد ذخیره شدن و بازیابی شدن قرار میگیرند. دقت کنید که فقط Interface های مربوط به Repository ها در این قسمت قرار میگیرند. پیاده سازی و یا همان Implementation آنها در لایه های مختلف دیگر از قبیل لایه زیرساخت و یا Infrastructureاتفاق میافتد. لایه زیرساخت حاوی مواردی است که اغلب مورد تغییر قرار میگیرند. این گونه از موارد باید از Application Core کاملا جدا بشوند. برخی از مواردی که در Infrastructure Layer قرار میگیرند حاوی User Interface و Automation Test ها و کدهای مربوط به Database Access و یا Network Access و یا File Access میباشد.

خوب این آموزش در رابطه با Domain Driven Design را در این قسمت به پایان میرسانیم. امیدواریم این آموزش چند قسمتی از وب سایت پروید مورد توجه تمامی دوستان قرار گرفته باشد. از شما دعوت میکنیم که از مقاله های و سری های آموزشی دیگری که در رابطه با Domain Driven Design و همچنین معماری پیازی بر روی وب سایت پروید منتشر میشود استفاده کنید. ضمناً توصیه میکنیم که از بسته های اموزشی مختلفی که در رابطه با Domain Driven Design بر روی وب سایت پروید منتشر شده اند نیز استفاده بفرمائید. لیست کامل قسمت های این آموزش را می توانید در مقدمه ای بر طراحی دامنه محور Domain Driven Design مطالعه بفرمایید.

مرتضی گیتی
بدون نظر

ارسال نظر

نظر
نام
ایمیل
وب سایت