در بخش پنجم از سری مطالب طراحی مبتنی بر دامنه (Domain Driven Design)، به بررسی موضوعات مرتبط با Tactical Design شامل مدلسازی (Modeling) و معماری (Architecture) میپردازیم. تمرکز ما در این بخش بر روی معماریهای Domain-Centric و نحوه طراحی سیستمهایی است که دارای پیچیدگیهای قابل توجه در منطق دامنه هستند.

در طراحی استراتژیک، این امکان وجود دارد که در Bounded Contextهای مختلف، با توجه به نیازمندیها، از معماریهای متفاوتی استفاده کنیم. بهعنوان مثال:
در این مطلب، تمرکز ما روی دسته دوم است: سیستمهایی با پیچیدگی بالا که نیازمند طراحی دقیق و معماری مبتنی بر دامنه هستند.
معماری سنتی: سه لایهای
در گذشته، معماری رایج به صورت سه لایهای (Presentation, Business, Data) طراحی میشد. در این ساختار، اغلب از الگویی به نام Transaction Script استفاده میشد که در آن، منطق برنامه به صورت رویهای (Procedural) در قالب Serviceها پیادهسازی میشد.
در این رویکرد، مدلهای دامنه، تنها شامل دادهها بودند و فاقد رفتار منطقی بودند؛ به این نوع مدلها Anemic Domain Model گفته میشود.
Rich Domain Model
در مدل دامنهی غنی (Rich Domain Model)، اشیاء دامنه، هم شامل داده هستند و هم رفتارهایی که مربوط به آن دادههاست. ویژگیهای این مدل:
در معماری Domain-Centric، چهار لایهی اصلی در نظر گرفته میشود:
در طراحی صحیح، باید بین دو نوع منطق تفکیک قائل شد:
مثال: هنگام ثبت پیشنهاد روی یک مزایده (Auction)، بخشهایی از فرایند مانند بارگذاری مزایده، بررسی باز بودن آن، مقایسه قیمت و ذخیرهسازی پیشنهاد جدید، شامل هر دو نوع منطق میشوند.
در تصویر زیر یک sequence diagram را مشاهده می کنید.

در بالای تصویر، لایه بندی که مدنظر بوده است، مشخص شده است.
همانطور که ملاحظه می کنید، در User Interface، یک TransferController قرار دارد که برای عملیات انتقال وجه، یک Application Service را فراخوانی می کند. Application Service از طریق FundsTransferService کار Orchestration را انجام می دهد و logic برنامه در Domain Layer اتفاق می افتد.
یکی از اصول کلیدی در طراحی معماری، اصل Dependency Inversion Principle (DIP) است. بر اساس این اصل:
فرض کنید در لایه BLL کلاس SalaryCalculationService داریم که برای محاسبه حقوق از کلاس TaxRepository در DAL استفاده میکند. این وابستگی به یک ماژول low level است که خلاف DIP است. راهحل چیست؟ تعریف یک Abstraction توسط ماژول high level و پیادهسازی آن در ماژول low level.
پکیج BLL را یک پکیج high level در نظر می گیریم. از این نظر که به مفاهیم business نزدیک تر است و موضوعات در آن مرتبط با business هستند. به این دلیل برای ما حجم پیچیدکی و اهمیت بیشتری دارد. پکیج DAL یک low level است. از این جهت که بیشتر technical است و policyهای business در آن کمتر دیده می شود.

اصل dependency inversion به این شکل عمل می کند که ماژول high level، سطح abstraction را تعریف می کند و از آن استفاده می کند. ماژول low level نیز آن abstraction را پیاده سازی می کند و از این طریق جهت وابستگی تغییر می کند. در نتیجه تغییرات نیز از سمت high level module به سمت low level module حرکت می کند.
معماریهایی مانند Hexagonal Architecture، Onion Architecture و Clean Architecture، همگی اهداف مشترکی را دنبال میکنند:
مفهوم Stability به هزینه تغییر مربوط میشود. اگر یک ماژول تغییر ناپذیر یا تغییر در آن دشوار باشد، گفته میشود که Stable است. در طراحی خوب، باید تلاش کنیم ماژولهای Stable وابسته به ماژولهای Unstable نباشند.
کامپوننتهایی که Stable هستند نباید به کامپوننتهایی وابسته باشند که زیاد تغییر میکنند.
کامپوننتهای Stable باید Abstract باشند تا بتوانند قابل توسعه و تغییر باشند.
در این نمودار، محل مناسب برای کامپوننتها ناحیهی سبز رنگ است که هم پایدار و هم abstract هستند. دو ناحیهی دیگر:

برای حل وابستگیها در Application Layer میتوان یک پکیج جداگانه با عنوان مثلاً Application.Contracts تعریف کرد که در آن تنها abstractionها قرار بگیرند. سپس لایه Application فقط پیادهسازی این قراردادها را انجام دهد. این کار باعث حفظ استقلال و انعطافپذیری در طراحی میشود.
در این بخش، تلاش کردیم تا با بررسی معماری Domain-Centric، تفکیک منطقهای مختلف، اصول طراحی پایدار و مفاهیم مدرن معماری، دید عمیقتری نسبت به پیادهسازی سیستمهای پیچیده کسبوکار ارائه دهیم. در بخشهای بعدی، وارد جزئیات بیشتری در رابطه با پیادهسازی Tactical Patternهای DDD خواهیم شد.