بررسی کاربرد Domain-driven design در اپلیکیشن های Asp.Net Core

در این درس از سری آموزش متنی و رایگان معماری های مفید در اپلیکیشن های Asp.Net Core قصد داریم در رابطه با کاربرد طراحی دامنه محور و یا Domain-driven design در Asp.Net Core صحبت کنیم. در ابتدای کار توصیه می‌کنیم از برخی از آموزش ها از قبیل بسته ی آموزش ویدئویی معماری پیازی و ساخت پروژه عملی با ASP.NET Core MVC و بسته ی آموزش ویدئویی کاربردی طراحی نرم افزار Domain Driven Design دیدن کنید. واژه Domain-driven design که اغلب به صورت DDD نوشته می شود یک روش چابک برای توسعه نرم افزارهایی است که تاکید زیادی بر روی business domain و یا حوزه تجاری دارند. Domain-driven design تاکید فراوانی را بر روی ارتباطات و تعاملات بین متخصصین و یا خبرگان دامنه که تحت عنوان business domain expert نیز شناخته می‌شوند لحاظ می‌کند. این ارتباط بین business domain expert ها و Developer هایی رخ می دهد که قرار است برنامه نهایی را تولید کنند. با استفاده کردن از این ارتباط می‌توان سیستمی را ایجاد کرد که بیش از هر سیستم دیگری شبیه به آن چیزی است که مشتری در نظر داشته است. برای مثال اگر شما در حال توسعه یک سیستم برای مدیریت کردن سهام بورس هستید یک business domain expert می‌تواند شخصی باشد که سالیان سال تجربه در حوزه سهام بورس دارد. استفاده کردن از DDD و یا Domain-driven design در پروژه‌های بسیار بزرگ و پیچیده توصیه می‌شود و استفاده کردن از آن در پروژه‌های کوچک تاثیر زیادی نخواهد داشت. از طرفی پیچیدگی‌های پیاده‌سازی Domain-driven design در پروژه های کوچک باعث ایجاد سربار می شود و مزیت زیادی را در اختیار برنامه نویسان قرار نخواهد داد.

در استفاده کردن از روش Domain-driven design تیم شما که متشکل از ذی‌نفعان غیرفنی و برنامه نویسان می باشد می بایست از یک زبان فراگیر و یا اصطلاحاً ubiquitous language برای حل مسائل مربوط به برنامه استفاده کنند. به عبارت دیگر واژگانی که برای توصیف مفاهیم واقعی مورد استفاده قرار می گیرند می بایست معادل‌سازی بشوند و تمامی افراد فعال در روند پیشبرد پروژه از واژگان یکسانی استفاده کنند. استفاده کردن از ubiquitous language و یا زبان فراگیر یک موضوع بسیار مهم به حساب می‌آید و در طراحی Domain model شما می بایست حتما در نظر گرفته شود.

منظور از Domain model نیز مجموعه‌ای از آبجکت ها می باشد که با تعامل پیدا کردن با یکدیگر رفتار کلی سیستم را تعریف می‌کنند. اشیا و یا Object های موجود در Domain model به طور کلی به دسته های زیر تقسیم می‌شوند:

  • موجودیت و یا Entity، منظور از Entity ها در واقع Object هایی است که شامل هویت و یا Identity می باشند. Entity ها اغلب با استفاده از یک کلید و یا Key در دیتابیس ذخیره شده و سپس بازیابی می شوند.
  • تجمیع و یا Aggregate ها، منظور از Aggregate ها گروهی از آبجکتهایی است که در کنار یکدیگر معنا پیدا می کنند و در قالب یک واحد تک در دیتابیس ذخیره شده و بازیابی می گردند.
  • منظور از Value Object مفاهیمی است که می‌توانند و اساس مجموعه‌ای از مقادیر مختلف با یکدیگر مقایسه شوند برای مثال بازه زمانی و یا تاریخی که تحت عنوان DateRange نیز شناخته می‌شود شامل نقطه شروع و نقطه پایان زمانی است و در قالب Value Object پیاده سازی می گردد.
  • رویدادهای دامنه و یا Domain event ها، منظور از Domain event ها اتفاقاتی است که در سیستم اتفاق می‌افتند و برای بخش‌های دیگری از سیستم‌ حائز اهمیت هستند.

به تمامی علاقه مندان به یادگیری مفاهیم Domain-driven design توصیه می‌کنیم که از آموزش متنی طراحی دامنه محور تکنیکی (Tactical Domain-Driven Design) و آموزش متنی طراحی دامنه محور راهبردی (Strategic Domain-Driven Design) دیدن کنید.

یکی از موضوعات بسیار مهم در طراحی یک Domain model مناسب در روش DDD کپسوله کردن رفتارهای پیچیده در مدل است. به عبارت دیگر Entity ها نباید لزوما مجموعه ای از پروپرتی ها باشند که در قالب کلاس های POCO پیاده سازی می شوند. زمانی که Domain model فاقد رفتار و یا Behavior باشد و فقط State و یا حالت سیستم را نشان بدهد به آن Domain model اصطلاحاً anemic می‌گویند. برای حل و فصل کردن مشکلات مربوط به چنین Domain model خاصی می توانید از بسته ی آموزش ویدئویی Domain Driven Design و ریفکتور کردن یک Anemic Domain Model دیدن کنید. استفاده کردن از anemic Domain model ها در روش Domain-driven design به هیچ وجه توصیه نمی شود.

علاوه بر انواع مختلفی که در یک Domain model وجود دارند، روش Domain-driven design مجموعه از الگوها را نیز مورد استفاده قرار می دهد که در ادامه به بررسی بسیار کوتاه چندی از آنها خواهیم پرداخت.

  • الگوی Repository، با استفاده از این الگو شما می توانید جزئیات مربوط به ذخیره سازی و بازیابی داده ها از بانک اطلاعاتی را Abstract کنید. در رابطه با این الگوی طراحی می توانید از آموزش متنی پیاده سازی صحیح الگوی Repository دیدن کنید.
  • الگوی طراحی Factory، با استفاده از این الگوی طراحی می توانید جزئیات مفصل و بسیار پیچیده ایجاد کردن Object ها را کپسوله کنید.
  • استفاده کردن از Domain event ها، از Domain event ها برای Decouple کردن رفتارهای وابسته از رفتارهای آغاز کننده استفاده می‌کنیم.
  • سرویس ها، از سرویس ها برای کپسوله کردن رفتارهای پیچیده و یا کپسوله کردن جزئیات پیاده‌سازی مربوط به زیرساخت برنامه و یا همان Infrastructure استفاده می‌کنیم.
  • الگوی Command، با استفاده از Command ها می‌توانیم جزئیات مربوط به اجرا کردن یک کامند و صادر کردن آن را در خود آن کامند Abstract کنید.
  • الگوی طراحی Specification، از این الگوی طراحی برای کپسوله کردن جزئیات مربوط به کوئری ها استفاده می کنیم. در این رابطه توصیه می کنیم از بسته ی آموزش ویدئویی پیاده سازی الگوی Specification در Domain Driven Design دیدن بفرمائید.

علاوه بر موارد ذکر شده روش Domain-driven design، استفاده کردن از معماری تمیز که پیشتر از آن صحبت کردیم را نیز توصیه می کند. استفاده کردن از معماری تمیز باعث ایجاد loose coupling و encapsulation می شود و از طرفی می تواند به سادگی با استفاده از unit test ها کدهای مختلف برنامه را مورد تست کردن قرار بدهد. در این رابطه توصیه می‌کنیم از بسته ی آموزش ویدئویی Unit Testing در سی شارپ دیدن کنید.

چه زمانی از Domain-driven design استفاده کنیم؟

همانطور که پیش‌تر گفتیم استفاده کردن از معماری و روش Domain-driven design برای برنامه های بزرگ و پیچیده که حاوی پیچیدگی تجاری و یا business complexity هستند مناسب می باشد. چنین برنامه هایی نیاز دارند که دانش domain experts ها را در ایجاد کردن Domain model لحاظ کنند. حتماً باید Behavior و یا رفتارهای خاصی در Domain model نیز وجود داشته باشند تا بتوانیم به سادگی business rules ها و تعاملات بین اجزای مختلف برنامه را لحاظ کنیم. در واقع اگر برنامه شما فقط وظیفه ذخیره کردن و بازیابی کردن State و یا حالت سیستم را در قالب رکوردهایی در دیتابیس دارد استفاده کردن از روش Domain-driven design به هیچ وجه توصیه نمی شود.

چه زمانی از Domain-driven design استفاده نکنیم؟

استفاده کردن از Domain-driven design در مدل سازی، معماری و برقراری ارتباط بین اجزای تشکیل دهنده برنامه پیچیدگی‌های خاص خود را دارد. برای برنامه های کوچک تر که فقط عملیات CRUD را انجام می دهند استفاده کردن از روش Domain-driven design توصیه نمی‌شود. اگر از روش Domain-driven design استفاده می کنید اما متوجه می‌شوید که Domain model شما به صورت anemic و بدون هیچ گونه behavior خاصی پیاده سازی شده است نیاز است که در رابطه با استفاده کردن از Domain-driven design بیشتر بررسی کنید. در چنین شرایطی یا باید اپلیکیشن مورد نظر را ریفکتور کرد و business logic مورد نظر را در Domain model قرار داد و آن را از دیتابیس و user interface جدا نمود و یا می بایست به طور کلی استفاده کردن از Domain-driven design را کنار گذاشت و از روشهای ساده تر استفاده نمود. علاوه بر این موضوع می توان از روش های ترکیبی نیز استفاده کرد. با استفاده از یک روش ترکیبی شما فقط زمانی از Domain-driven design استفاده می کنید که میخواهید حوزه‌های پیچیده‌تر و تراکنشی سیستم را پیاده سازی کنید. به عبارت دیگر برای سناریو های ساده که عملاً CRUD هستند می توان از معماری های ساده تری استفاده کرد. به عنوان مثال در بسیاری از شرایط نیازی به لحاظ کردن Constraint هایی بر روی Aggregate ها نداریم و می تواند و در چنین شرایطی میتوان Aggregate ها را فقط به منظور کوئری گرفتن از داده ها و نشان دادن آنها در قالب یک گزارش و یا یک نمودار مورد استفاده قرار داد.