ویرگول
ورودثبت نام
Mohsen Farokhi - محسن فرخی
Mohsen Farokhi - محسن فرخیSenior .NET Developer
Mohsen Farokhi - محسن فرخی
Mohsen Farokhi - محسن فرخی
خواندن ۵ دقیقه·۳ سال پیش

Domain Driven Design - بخش پنجم

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

Domain Driven Design
Domain Driven Design

معماری در طراحی استراتژیک

در طراحی استراتژیک، این امکان وجود دارد که در Bounded Contextهای مختلف، با توجه به نیازمندی‌ها، از معماری‌های متفاوتی استفاده کنیم. به‌عنوان مثال:

  • اگر یک Bounded Context ساده و صرفاً شامل عملیات‌های CRUD باشد، می‌توان از طراحی ساده‌تری استفاده کرد.
  • اما در مقابل، زمانی که با یک Context پیچیده با قوانین وابسته و متنوع، Invariant‌های متعدد و منطق‌های پیچیده مواجه هستیم، نیاز به رویکردی ساختارمندتر داریم.

در این مطلب، تمرکز ما روی دسته دوم است: سیستم‌هایی با پیچیدگی بالا که نیازمند طراحی دقیق و معماری مبتنی بر دامنه هستند.

معماری Domain-Centric

معماری سنتی: سه لایه‌ای

در گذشته، معماری رایج به صورت سه لایه‌ای (Presentation, Business, Data) طراحی می‌شد. در این ساختار، اغلب از الگویی به نام Transaction Script استفاده می‌شد که در آن، منطق برنامه به صورت رویه‌ای (Procedural) در قالب Serviceها پیاده‌سازی می‌شد.

در این رویکرد، مدل‌های دامنه، تنها شامل داده‌ها بودند و فاقد رفتار منطقی بودند؛ به این نوع مدل‌ها Anemic Domain Model گفته می‌شود.

Rich Domain Model

در مدل دامنه‌ی غنی (Rich Domain Model)، اشیاء دامنه، هم شامل داده هستند و هم رفتارهایی که مربوط به آن داده‌هاست. ویژگی‌های این مدل:

  • منطق کسب‌وکار درون اشیاء دامنه محصور شده (Encapsulated) است.
  • فاقد وابستگی به فریم‌ورک‌ها و تکنولوژی‌های زیرساختی هستند.
  • قابلیت تست بالایی دارند.
  • از زبان دامنه (Ubiquitous Language) استفاده می‌کنند.
  • و Application Logic در این لایه وجود ندارد.

لایه‌های معماری Domain-Centric

در معماری Domain-Centric، چهار لایه‌ی اصلی در نظر گرفته می‌شود:

  1. User Interface (UI)
    نمایش داده‌ها و دریافت فرمان‌ها از کاربر.
  2. Application Layer
    مسئول اجرای Use Caseها از طریق واگذاری (Delegation) به سایر لایه‌ها. فقط orchestration انجام می‌دهد.
  3. Domain Layer
    قلب سیستم است؛ منطق کسب‌وکار و قوانین در این لایه پیاده‌سازی می‌شوند. کاملاً مستقل از زیرساخت و فریم‌ورک است.
  4. Infrastructure Layer
    مسئول تعامل با دیتابیس، سیستم فایل، لاگرها و سرویس‌های خارجی است.

تفکیک منطق‌ها

در طراحی صحیح، باید بین دو نوع منطق تفکیک قائل شد:

  • Application Logic: مربوط به ترتیب و مدیریت اجرای مراحل یک Use Case.
  • Business Logic: شامل قوانین و رفتارهای دامنه که در Domain Layer پیاده‌سازی می‌شود.

مثال: هنگام ثبت پیشنهاد روی یک مزایده (Auction)، بخش‌هایی از فرایند مانند بارگذاری مزایده، بررسی باز بودن آن، مقایسه قیمت و ذخیره‌سازی پیشنهاد جدید، شامل هر دو نوع منطق می‌شوند.

در تصویر زیر یک sequence diagram را مشاهده می کنید.

در بالای تصویر، لایه بندی که مدنظر بوده است، مشخص شده است.

همانطور که ملاحظه می کنید، در User Interface، یک TransferController قرار دارد که برای عملیات انتقال وجه، یک Application Service را فراخوانی می کند. Application Service از طریق FundsTransferService کار Orchestration را انجام می دهد و logic برنامه در Domain Layer اتفاق می افتد.


بررسی وابستگی‌ها و اصل Inversion

یکی از اصول کلیدی در طراحی معماری، اصل Dependency Inversion Principle (DIP) است. بر اساس این اصل:

  • ماژول‌های سطح بالا (High-Level Modules) نباید به ماژول‌های سطح پایین وابسته باشند.
  • هر دو باید به Abstraction وابسته باشند.

مثال:

فرض کنید در لایه 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 Principle
Dependency Inversion Principle

اصل dependency inversion به این شکل عمل می کند که ماژول high level، سطح abstraction را تعریف می کند و از آن استفاده می کند. ماژول low level نیز آن abstraction را پیاده سازی می کند و از این طریق جهت وابستگی تغییر می کند. در نتیجه تغییرات نیز از سمت high level module به سمت low level module حرکت می کند.


معماری‌های مدرن: Hexagonal، Onion و Clean Architecture

معماری‌هایی مانند Hexagonal Architecture، Onion Architecture و Clean Architecture، همگی اهداف مشترکی را دنبال می‌کنند:

  • استقلال از UI
  • استقلال از دیتابیس و سرویس‌های خارجی
  • امکان تست آسان
  • جداسازی concerns به‌صورت ساختارمند
  • رعایت Dependency Rule: وابستگی‌ها فقط به سمت داخل (Domain) است.

اجزای اصلی در Clean Architecture

  • Entities (Domain Model)
  • Use Cases (Application Layer)
  • Interface Adapters (Controller, Presenter, Gateway)
  • Frameworks & Drivers (UI, DB, External Services)

اصل پایداری در طراحی

Stability چیست؟

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

Stable Dependency Principle (SDP)

کامپوننت‌هایی که Stable هستند نباید به کامپوننت‌هایی وابسته باشند که زیاد تغییر می‌کنند.

Stable Abstraction Principle (SAP)

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


نمودار Stability vs Abstractness

در این نمودار، محل مناسب برای کامپوننت‌ها ناحیه‌ی سبز رنگ است که هم پایدار و هم abstract هستند. دو ناحیه‌ی دیگر:

  • Zone of Pain: Stable و Concrete → تغییر سخت
  • Zone of Uselessness: Instable و Abstract → بدون کاربرد واقعی

پیشنهاد ساختار بهتر برای Application Layer

برای حل وابستگی‌ها در Application Layer می‌توان یک پکیج جداگانه با عنوان مثلاً Application.Contracts تعریف کرد که در آن تنها abstractionها قرار بگیرند. سپس لایه Application فقط پیاده‌سازی این قراردادها را انجام دهد. این کار باعث حفظ استقلال و انعطاف‌پذیری در طراحی می‌شود.

در این بخش، تلاش کردیم تا با بررسی معماری Domain-Centric، تفکیک منطق‌های مختلف، اصول طراحی پایدار و مفاهیم مدرن معماری، دید عمیق‌تری نسبت به پیاده‌سازی سیستم‌های پیچیده کسب‌وکار ارائه دهیم. در بخش‌های بعدی، وارد جزئیات بیشتری در رابطه با پیاده‌سازی Tactical Patternهای DDD خواهیم شد.

domain driven designarchitecture
۴
۰
Mohsen Farokhi - محسن فرخی
Mohsen Farokhi - محسن فرخی
Senior .NET Developer
شاید از این پست‌ها خوشتان بیاید