رویکرد "طراحی دامنه محور" یا همان "Domain-Driven Design"، نخستین بار توسط Eric Evans در کتابی تحت همین عنوان در سال 2004 بیان شد. به طور کلی هدف کلی این رویکرد این است که بگوید ساختار و زبان کد یک نرم افزار، باید با حوزه کسب و کار آن مطابقت داشته باشد. طراحی دامنه محور (DDD) رویکردی برای توسعه نرم افزار برای نیازهای پیچیده با اتصال عمیق پیاده سازی به یک مدل در حال تکامل است. به کمک این امر باعث می شود تا پیچیدگی ای که در قلب نرم افزار وجود دارد ساده تر شوند. DDD مناسب تیم ها و نرم افزار های بزرگ است. در همین راستا Eric Evans در خصوص DDD می گوید:
برای ایجاد نرم افزار خوب، باید بدانید که آن نرم افزار چیست. شما نمی توانید یک سیستم نرم افزاری بانکی ایجاد کنید مگر اینکه درک خوبی از چیستی بانکداری داشته باشید، باید حوزه بانکداری را درک کنید.
در ادامه DDD را به طور مفصل معرفی خواهیم کرد.
فرض کنید می خواهیم یک "معجون" درست کنیم. برای درست کردن معجون باید مواد مختلفی مانند: گردو، شیر، خرما، عسل با هم مخلوط شوند و مدتی هم در یخچال بمانند. اما این مواد به صورت مجزا هم قابل خوردن هستند. فرض کنید شخصی برای شما یک معجون می آورد و شخص دیگری یک سینی می آورد که در آن گردو، شیر، خرما و عسل به صورت تفکیک شده از هم قرار دارند. حال اضافه و کم کردن یک ماده غذایی دیگر به کدام یک از این دو راحت تر است؟ واضح است که شما نمی توانید خرما را از داخل معجون بیرون بکشید! اما می توانید خرما را از سینی بردارید و به راحتی با ماده دیگری جایگزین کنید. سینی در این مثال هر کدام از مواد غذایی حکم Domain را دارند و ظرف سینی هم همان مفهوم کلی DDD است. در این صورت اضافه یا کم کردن قطعات دیگر به آسانی انجام خواهد پذیرفت.
به عنوان مثال ممکن است حوزه یک بخش از نرم افزار ارسال پیام است، یا حوزه دیگر در یک بخش دیگر از نرم افزار دریافت اطلاعات از یک سیستم دیگر است. در ادامه با مفاهیم Domain و DDD را به طور شفاف تری متوجه می شویم.
برای درک تخصصی تر DDD با چند تعریف آشنا می شویم:
ابتدا با مفهوم Domain یا دامنه آشنا می شویم، Domain (دامنه)، یک حوزه ای از دانش و فعالیت است که منطق برنامه در آن می چرخد. بیاید این جمله را با هم بررسی کنیم. دامنه به حوزه موضوعی اشاره دارد که برنامه در آن قسمت حول آن می چرخد و کاربر برای آن حوزه است که از آن برنامه استفاده می کند. دلمنه بیانگر حوزه کلی یک برنامه است. به عنوان مثال در دنیای واقعی، "ماشین" یک دامنه است که شامل چرخ و چراغ و موتور است. اجزای ذکر شده به طور مفهومی با یکدیگر ارتباط دارند. گاهی اوقات در یک سازمان ممکن است چندین دامنه وجود داشته باشد. به عنوان مثال، یک شرکت می تواند در فروش، حمل و نقل و تعمیرات به طور همزمان فعال باشد.
یک مفهوم دیگر داریم به نام Context، مفهوم Context به این صورت تعریف شده است: محیطی که در آن یک کلمه یا یک عبارت ظاهر می شود که معنای آن را تعیین می کند را Context می نامند. در واقع Context حداکثر اندازه ای است که یک مدل می تواند بدون به خطر انداختن یکپارچگی مفهومی آن گسترش یابد.
برای کنار آمدن با یک مدل بزرگ، میتوان مدل را به مناطق مختلف تقسیم کرد که ما آن را Bounded Context مینامیم. یک سازمان را می توان به یک بخش فروش و یک بخش پشتیبانی تقسیم کرد که هر کدام در چارچوب خود عمل می کنند. با کار در یک Bounded Context کار آسان تر و بهتر سازماندهی می شود.
حال بیاید یک مثال را در حوزه نرم افزار و به کمک شکل زیر به صورت تخصصی تر با هم بررسی کنیم:
همانطور که در شکل فوق مشخص است، دو تا Context کلی داریم: Support (پشتیبانی) و Sale (فروش). نکته ای که وجود دارد این است دور هر کدام از آن ها یک خط کشیده شده است تا از هم تفکیک شوند، لذا به همین دلیل به هر کدام از آن ها Bounded Context هم گفته می شود. اما اینطور نیست که آن ها به صورت کامل از هم جدا باشند و هیچ ارتباطی با هم نداشته باشند. در شکل فوق نیز هر کدام از Bounded Context ها تداعی گر مفهوم یک حوزه خاص (مانند پشتیبانی و فروش) هستند که در نقاط مشتری و محصول با یکدیگر ارتباط دارند.
اگر با معماری SOA یا همان معماری سرویس گرا آشنایی داشته باشید، متوجه می شود که DDD یکی از عناصر کلیدی معماری سرویس گرا است چرا که به encapsulate (پنهان سازی) کردن منطق و قواعد کسب و کار در domain object (اشیا دامنه) کمک می کند. همچنین در طراحی دامنه محور، لایه دامنه، یکی از لایه های رایج در معماری چند لایه شی گرا است.
از این رو، توسعه دهندگان یک Domain Model (مدل دامنه) می سازند: Domain Model سیستمی است از مفاهیم و انتزاع ها که جنبه های انتخابی یک دامنه را توصیف می کند و می تواند برای حل مشکلات مربوط به آن دامنه استفاده شود.
مارتین فاولر درباره Domain Model این گونه می گوید:
مارتین فاولر: یک Domain Model، شبکه ای از اشیاء (Objects) به هم پیوسته را ایجاد می کند، که در آن هر شی نشان دهنده یک فرد معنادار است، چه به بزرگی یک شرکت یا به کوچکی یک خط در فرم سفارش.
شکل زیر مصداقی از حرف او است:
همانطور که مشاهده می شود، هم رفتار و هم داده ها در بر گرفته شده اند.
یک Domain Event (رویداد دامنه) نشان دهنده اتفاق مهمی است که از دیدگاه کسب و کار در سیستم رخ داده است. این نوع رویداد ها نمی توانند بعد از این که اتفاق افتادند حذف یا به روز بشوند. "اضافخ کردن محصول به سبد" و "حذف کردن محصول از سبد" نمونه هایی هستند که در یک دامنه قرار دارند. آن ها همچنین مبنای الگوی هایی مانند Event Sourcing هستند.
یک DDD Aggregate مجموعه ای از اشیاء دامنه (Domain Object) است که می تواند به عنوان یک واحد در نظر گرفته شود. حواستان باشد که Aggregate را با Collection ها اشتباه نکنید! Aggregate ها مفاهیم دامنه هستند (مانند سفارش، بازدید از کلینیک، لیست پخش) در صورتی که Collection ها عمومی هستند. لذا Aggregate ها معمولا درون خود چندین Collection به همراه چند فیلد ساده دیگر دارند. مثلا یک سفارش و لیست کالا های درون آن سفارش را در نظر بگیرید. این ها از هم جدا هستند، اما بهتر است که سفارش (همراه با لیست کالاهای درون آن) به عنوان یک Aggregate در نظر گرفته شود.
هدف این جنبههای طراحی دامنه محور، تقویت ubiquitous language (زبان فراگیر) است، به این معنی که مدل دامنه باید زبان مشترکی را تشکیل دهد که Domain Expert ها (متخصصان دامنه) برای توصیف نیازمندیهای سیستم، کاربران تجاری، حامیان مالی و توسعهدهندگان از آن استفاده کنند. ubiquitous language (زبان فراگیر) حامل جنبه هایی از طراحی است که در کد ظاهر نمی شوند ولی قابل درک هستند. در واقع زبان فراگیر یک کانال حاوی اطلاعاتی است که بین توسعه دهندگان و متخصصان دامنه و نرم افزار در جریان است و زبان مشترک بین آن ها است:
همانطور که مشاهده می شود، زبان فراگیر یک زبان مشترک میان زبان توسعه دهندگان و زبان کسب و کار است.
یک مثال دیگر از زبان فراگیر ببینم تا راحت تر مفاهیم ذکر شده را درک کنیم:
همانطور که مشاهده می شود، زبان فراگیر یا همان Ubiquitous Language یک زبان واحد است که از آن در همه جا برای بیان یک مدل استفاده می شود. همه ی اعضای تیم باید بتوانند در مورد هر اصطلاح خاص بدون ابهام توافق کنند و نیازی به ترجمه نباشد.
اگر با مفهوم OOP یا همان برنامه نویسی شی گرا آشنایی داشته باشید، حتما متوجه شده اید که DDD شباهت زیادی با OOP دارد. اما آن چه DDD دارد ولی در OOP موجود نیست، همین زبان فراگیر است! چرا که این زبان فراگیر کمک می کند تا درک مساله برای تمام افراد فنی و غیر فنی راحت تر باشد اما در OOP فقط افراد فنی می توانند کدی که توسعه داده شده است را درک کنند.
استفاده از این دو به همراه هم رایج است. رویکردهای DDD تنها در صورتی باید اعمال شوند که در حال پیادهسازی میکروسرویسهای پیچیده با قوانین کسب و کار مهم هستیم. مسئولیتهای سادهتر، مانند سرویس CRUD، با رویکردهای سادهتر قابل مدیریت هستند. در هنگام طراحی و تعریف یک میکروسرویس، این موضوع که کجا باید مرزها را ترسیم کنیم بسیار مهم است.
الگوهای DDD به درک پیچیدگی دامنه ها کمک می کند. برای مدل دامنه برای هر زمینه محدود، موجودیتها، اشیاء ارزشی و تجمیعهایی که دامنه شما را مدل میکنند شناسایی و تعریف میکنید. در این حین یک مدل دامنه طراحی و اصلاح می شود که در محدوده ای قرار می گیرد که زمینه را مشخص کند. این موضوع در قالب یک میکروسرویس آشکار است. اجزای درون آن مرزها در نهایت به میکروسرویس ها تبدیل می شوند، اگرچه در برخی موارد یک میکروسرویس BC یا کسب و کار می تواند از چندین سرویس فیزیکی تشکیل شده باشد. DDD و میکروسرویس ها هر دو در مورد مرزها هستند.
اکثر برنامه های سازمانی با پیچیدگی تجاری و فنی قابل توجه توسط چندین لایه تعریف می شوند. ( توجه داشته باشید که لایه ها یک چیز منطقی هستند و به استقرار سرویس مربوط نیستند) آنها برای کمک به توسعه دهندگان برای مدیریت پیچیدگی کد وجود دارند. لایه های مختلف (مانند لایه مدل دامنه در مقابل لایه ارائه و غیره) ممکن است انواع مختلفی داشته باشند که ترجمه بین آن انواع را الزامی می کند. به عنوان مثال، یک موجودیت می تواند از پایگاه داده بارگذاری شود. سپس بخشی از آن اطلاعات، یا مجموعهای از اطلاعات شامل دادههای اضافی از سایر نهادها، میتواند از طریق یک REST Web API به UI مشتری ارسال شود. نکته اینجاست که موجودیت دامنه در لایه مدل دامنه قرار دارد و نباید به مناطق دیگری که به آن تعلق ندارد مانند لایه ارائه منتشر شود. این نمات در شکل زیر وجود دارند:
همانطور که در شکل فوق مشخص است، میکروسرویس مربوط به سفارش از 3 لایه تشکیل شده است. یک لایه مربوط به API، یک لایه مربوط به دامنه و یک لایه هم برای زیر ساخت. هر کدام از این لایه ها نیز به چندین زیرلایه تقسیم شده اند.
همانطور که دیدیم، DDD یک تکنولوژی نیست، بلکه یک رویکرد است که اصول و قواعدی را معرفی می کند تا تصمیم گیری در حوزه های تجاری پیچیده را آسان تر کند. استفاده از DDD باعث می شود که افراد فنی و غیر فنی حرف یکدیگر را بهتر بفهمند چرا که DDD، پیچیدگی های قواعد و منطق کسب و کار را در دل یک سری مدل پنهان می کند. اما استفاده از DDD هزینه بر است، لذا استفاده از آن برای پروژه های کوتاه مدت یا پروژه هایی که پیچیدگی دامنه بالایی ندارند مناسب نیست.
[1] وبسایت https://www.infoq.com/articles/ddd-in-practice
[2] وبسایت https://www.lucidchart.com/blog/domain-driven-design-introduction
[3] وبسایت https://simpleprogrammer.com/importance-domain-driven-design
[4] وبسایت https://en.wikipedia.org/wiki/Domain-driven_design
[5] کتاب "Domain-Driven Design Tackling Complexity in the Heart of Software" اثر Eric Evans منتشر شده در سال 2003
[6] وبسایت https://martinfowler.com
[7] وبسایت https://domaindrivendesign.org
[8] وبسایت https://codezup.com/what-is-domain-driven-design-ddd-pros-cons
[9] وبسایت https://towardsdatascience.com/what-is-domain-driven-design-5ea1e98285e4
این مطلب، بخشی از تمرینهای درس معماری نرمافزار در دانشگاه شهیدبهشتی است