هنگامی که از معماری نرم افزار یک سامانه صحبت می کنیم، می خواهیم بدانیم آن سامانه چیست و چگونه رفتار می کند. معماری نرم افزار این امکان را به ما می دهد تا درباره ی سامانه و توسعه ی پروژه ی آن دید خوبی داشته باشیم و در تعریف کارها و وظایف و همچنین تیم بندی، بهتر عمل کنیم. تعاریف مختلف و زیادی از معماری نرم افزار وجود دارد ولی همه ی آنها به اصول مشترکی اشاره می کنند:
معماری نرم افزار مجموعه ای از ساختارهای مورد نیاز برای پیشبرد و توسعه ی نرم افزار است که تمام المان های نرم افزار و رابطه ی آنها با یکدیگر و همچنین ویژگی های این المان ها و رابطه ها را مشخص می کند.
با طراحی یک معماری مفید و تاثیرگذار می توانیم ریسک های طراحی را مشخص کنیم و در مسیر توسعه، آنها را خنثی نماییم.
حال که به صورت مختصر با معماری نرم افزار آشنا شدیم بهتر است درباره ی طراحی یکی از معماری های مهم و تاثیرگذار نرم افزاری صحبت کنیم.
DDD چیست؟
طراحی معماری DDD که مخفف Domain Driven Design می باشد، بیشتر در حوزه ی ارتباط و نگاشت مفاهیم حوزه ی کسب و کار با تولیدات و خروجی های نرم افزاری می باشد. به قول معروف، هر وقت جمله " Domain Driven Design" را می شنوید، باید آن را به عنوان "Business Problem Driven Design" در نظر بگیرید.
بیشتر نوشته ها و مقالات پیرامون این موضوع بر اساس کتاب "Domain Driven Design" نوشته یEric Evans بوده است که شروع کننده ی جریان این نوع معماری می باشد. این کتاب که جنبه های مدل سازی و طراحی دامنه را عمدتاً از نقطه نظر مفهومی و طراحی پوشش می دهد، در مورد عناصر اصلی DDD مانندEntity ،Value Object ، Service و غیره بحث می کند یا در مورد مفاهیمی مانند یکپارچگی استفاده از زبان برنامه، محدوده های مفاهیم (Bounded Context) و لایه هایی برای جلوگیری از خرابی و انحراف برنامه (Anti-Corruption Layer) صحبت می کند. به این مفاهیم در ادامه ی مقاله می پردازیم.
برای تعریف طراحی دامنه محور یا همان DDD، ابتدا باید منظورمان از دامنه را در این زمینه (و به طور کلی در توسعه نرم افزار) مشخص کنیم. تعریف عمومی از دامنه این است: "حوزه دانش یا فعالیت". با کمی عمیق شدن، دامنه در حوزه مهندسی نرم افزار معمولاً به موضوع و هدفی که برنامه ی نرم افزاری در آن اعمال می شود، اشاره دارد. به عبارت دیگر، در طول توسعه ی برنامه، دامنه، حوزه ی دانش و فعالیتی است که منطق برنامه در اطراف آن قرار گرفته است. دامنه، ایده ها، دانش و داده های مسئله ای است که می خواهید آن را حل کنید.
مفهوم بعدی که باید با آن آشنا شویم، مدل می باشد. وقتی ما میتوانیم مسئله ای را مدل کنیم یعنی توانستیم در مسیر حل آن قدم برداریم. زیرا با مدل کردن توانستیم یه نمای کلی از تمام زوایای آن داشته باشیم و ساختارمند با مسئله رفتار کنیم.
بنابراین اگر دامنه را حوزه ی فعالیت و کسب و کار خود بدانیم و مدل را راه حل آن، پس Domain Model، دانش ساخت یافته از مسئله یا مسئله های کسب و کار ماست که در آن تمام مفاهیم، موجودیت ها و ارتباط های آنها مشخص می شود. Domain Model می تواند دیاگرام، مستند نوشته شده درباره ی مسئله ها، واژه نامه ی مسئله و یا حتی کدی که به صورت مثال بیان شده است، باشد.
اصطلاح رایج دیگری که در طول توسعه نرم افزار استفاده می شود، لایه دامنه (domain layer) یا منطق دامنه (domain logic) است که ممکن است برای بسیاری از توسعه دهندگان بیشتر به عنوان business logic شناخته شود. Business logic یک برنامه به قوانین سطح بالاتر برای نحوه تعامل اشیا objects برای ایجاد و اصلاح مدل ها اشاره دارد.
قبل از بررسی ملاحظات مختلف معماری DDD، بیایید نگاهی به ویژگی های یک Domain Model غنی بیاندازیم:
- مدل دامنه باید بر روی یک دامنه خاص تجاری متمرکز باشد. این باید با مدل کسب و کار، استراتژی ها و فرایندهای تجاری همسو باشد.
- باید از دامنه های دیگر تجاری و همچنین لایه های دیگر در معماری برنامه جدا و ایزوله باشد.
- باید قابل استفاده مجدد باشد تا از هرگونه مدل تکراری و پیاده سازی مجدد عناصر اصلی حوزه کسب و کار اجتناب شود.
- این مدل باید به صورتی طراحی شود که با سایر لایه های دیگر مانند لایه ی دیتابیس، وابستگی زیادی نداشته باشد.
- باید واضح، خلاصه و با مرزهای مشخص با سایر لایه ها باشد تا بتوان به راحتی آن را اصلاح یا تست کرد.
- باید به چارچوب ها و تکنولوژی ها و زیر ساخت ها وابستگی کمی داشته باشد تا مجبور نشویم که یک چارچوب قدیمی را با بدی های آن زنده نگه داریم.
می تونی خیلی سریع با کارراههی "برنامه نویس Front-End شو" وارد دنیای برنامه نویسی وب بشی! |
بعد از اینکه دامنه و ویژگی های یک دامنه ی خوب را شناختیم، معماری ای را باید معرفی کنیم که براساس این دامنه ها پیش برود. این معماری به فراِیندهای تجاری و تیم های توسعه دهنده کمک میکند تا محصولات جدید را با سرعت بهتری توسعه دهند زیرا DDD کمک میکند تا تمام logic business ها و قوانین در یک دامنه کپسوله شود.
حال بهتر است با مفاهیم اصلی و تعاریفی که درباره ی DDD گفته شده است آشنا شویم تا شناخت خوبی از آن داشته باشیم:
- یکپارچگی زبان (Ubiquitous language)
- لایه ها (Layers)
- محدوده ی مفاهیم (Bounded Contexts)
- لایه هایی برای جلوگیری از انحراف برنامه (Anti-Corruption Layer)
- هسته ی مشترک (Shared Kernel)
- زیردامنه ی عمومی (Generic Subdomain)
یکپارچگی زبان (Ubiquitous language)
ایده ی اصلی یکپارچکی زبان همراستا و همسو کردن برنامه کاربردی یا تکنولوژی با کسب و کار می باشد. یک مشکل مکرر در توسعه نرم افزار، درک کد است، این که کد برنامه چیست، چه کاری انجام می دهد، چگونه آن را انجام می دهد، چرا آن را انجام می دهد و ... . اگر از اصطلاحاتی متفاوت از اصطلاحاتی که دامنه معرفی کرده است، استفاده شود، درک کد بسیار پیچیده خواهد شد و انتقال مفاهیم بین متخصصان تعریف دامنه و توسعه دهندگان به سختی اتفاق می افتد. بیشتر این ابهام ها را می توان با نامگذاری مناسب کلاسها، متد ها و ماژولها حل کرد و دقیق معین کرد یک شی چیست و یک متد در مفهوم یک دامنه چه کاری را جلو می برد.
لایه ها (Layers)
معمولا معماری برنامه ها شامل چهار لایه مفهومی زیر است:
- User Interface: این لایه مسئول ترسیم صفحاتی است که کاربران برای تعامل با برنامه و ترجمه ورودی های کاربر به برنامه استفاده می کنند.
- Application Layer: این لایه در واقع موجودیت ها و اشیاء یک دامنه را در کنار هم قرار می دهد تا بتوانند نیازی که کاربر دارد یا همان use case ها را رفع کنند.
- Domain Layer: لایه ای که قلب سامانه می باشد و تمام بخش های business logic را در بر می گیرد. مانند موجودیت ها، سرویس ها، event ها، و هر آنچه که مربوط به منطق برنامه می باشد.
- Infrastructure: لایه ای با قابلیت ها و تکنولوژی هایی که باید داشته باشیم تا بتوان نیازهای لایه های بالاتر را رفع کرد.
در شکل زیر لایه ها و ارتباط های آنها را می بینید.
محدوده ی مفاهیم (Bounded Contexts)
در یک برنامه سازمانی، مدل می تواند رشد زیادی کند و همچنین تیمی که روی پایه کد کار می کند نیز رشد کند. این مسئله ما را به سمت دو مشکل سوق می دهد:
- کدی که یک توسعه دهنده میخواهد با آن کار کند هر اندازه بزرگتر باشد، شناخت آن کد سخت تر و احتمال به وجود آمدن خطا در کد بیشتر می شود.
- - کار کردن چند توسعه دهنده بر روی یک کد، نیاز به هماهنگی را بیشتر می کند و احتمال تداخل بیشتر می شود.
راه حل معمول برای این مشکل، تجزیه به قطعات کوچکتر است و این دقیقاً همان جایی است که محدوده ی مفاهیم معنی پیدا می کند و باید برای مرزهای آن تعریفی وجود داشته باشد. این تجزیه و ایزوله کردن می تواند با جدا کردن منطق فنی برنامه، تفکیک کد، تفکیک شمای دیتابیس و یا تفکیک و ایجاد تیم ها به وقوع بپیوندد.
لایه هایی برای جلوگیری از انحراف برنامه (Anti-Corruption Layer)
این لایه در واقع یک میان افزار (middleware) بین دو زیر سیستم است و برای جداسازی دو زیر سیستم مورد استفاده قرار می گیرد و باعث می شود که آنها به جای وابستگی مستقیم به یکدیگر به این لایه میانی وابسته باشند. به این ترتیب، اگر یکی از زیر سیستم ها را رفکتور یا کاملاً جایگزین کنیم، فقط باید لایه Anti-Corruption را به روز کنیم و زیر سیستم دیگر را دست نخورده بگذاریم.
هنگامی این امر به صورت ویژه مفید است که سیستم جدیدی داشته باشیم که باید آن را با یک سیستم قدیمی ادغام کنیم. برای اینکه اجازه ندهیم ساختار قدیمی نحوه طراحی سیستم جدید را تعیین کند، این لایه را ایجاد می کنیم که API زیر سیستم قدیمی را با نیازهای زیر سیستم جدید تطبیق دهد.
هسته ی مشترک (Shared Kernel)
در برخی شرایط، علی رغم تمایل ما به داشتن اجزای کاملاً جدا و تفکیک شده، منطقی است که برخی از دامنه ها توسط چندین مولفه به اشتراک گذاشته شوند. این نوع دامنه ها اجازه می دهند تا اجزای دیگر از یکدیگر جدا شوند. این نوع دامنه ها که با همان کد مشترک ایجاد می شوند، هسته مشترک نام دارند.
به عنوان مثال، در مورد رویدادهایی که توسط یک کامپوننت ایجاد شده و توسط یک یا چند کامپوننت دیگر به آنها گوش داده می شود، می توان به هسته ی مشترک فکر کرد. هرچند، ما باید هسته مشترک را کوچک نگه داریم و هنگام تغییر آن بسیار مراقب باشیم تا ناخواسته کدهای دیگر را با استفاده از آن تحت تاثیر قرار ندهیم. مهم است که کد موجود در هسته مشترک بدون مشورت با تیم های توسعه دهنده دیگری که از آن استفاده می کنند تغییر نکند.
زیردامنه ی عمومی (Generic Subdomain)
زیر دامنه بخشی کاملاً جدا شده و ایزوله از یک دامنه است. زیر دامنه عمومی زیر دامنه ای است که مخصوص برنامه ی ما نیست و می تواند در هر برنامه ی مشابهی مورد استفاده قرار گیرد. مثلا اگر برنامه ای داشته باشیم که بخشی از آن مربوط به امور مالی باشد، شاید بتوانیم از یک کتابخانه مالی موجود، استفاده کنیم.
بعد از اینکه با این مفاهیم و تعاریف آشنا شدیم سوالی پیش می آید که چگونه شروع کنیم؟
چگونه DDD را شروع کنیم ؟
- اولین نکته ای که در همه ی مراحل باید رعایت کنیم این است که پیوسته با آن کسی که متخصص به دامنه ها و آشنا به فضای کسب و کار می باشد، در ارتباط و بحث باشیم.
- در کل دامنه و در ارتباط گرفتن با سایر اعضا یکپارچگی زبان (Ubiquitous language) را رعایت کنیم. این یکپارچگی باید در صحبت ها، کد زنی ها، دیاگرام ها، مستند نویسی ها و حتی شمای دیتابیس وجود داشته باشد.
- سپس درباره ی مسئله ای که کسب و کار ما حول آن می چرخد اطلاعات کسب کنیم و دامنه را تشخیص دهیم. باید یک نمای کلی از فضای مسئله به دست بیاوریم.
- دامنه ها را به زیر دامنه ها خرد کنیم و برای زیر دامنه ها محدوده ی مفاهیم (Bounded contexts) در نظر بگیریم.
- در بهترین حالت برای هر محدوده، تیم مجزا و همچنین دیتابیس و پایگاه کد مجزا در نظر بگیریم.
- مسائلی ممکن است پیش بیاید که زیر دامنه ها و محدوده ها را باهم درگیر کند، شاید نیاز باشد به یک هسته ی مشترک (Shared Kernel) فکر کنیم. فقط باید به این نکته توجه داشت که تغییرات درShared Kernel باید با توافق و هماهنگی همه ی زیر دامنه ها باشد.
در DDD از چه مواردی باید اجتناب کنیم؟
- معمولاً مدل کردن داده اولین چیزی است که یک طراح یا توسعه دهنده ی نرم افزار انجام می دهد زیرا همیشه مهمترین مورد در نزد آنها، داده می باشد که باید به طریقی نمایش داده شود ولی اگر با DDD شروع می کنید، باید این طرز فکر را تغییر دهید. داده ها به خودی خود معنی ندارند و این منطق برنامه هست که به داده ها معنا می دهد. ممکن است داده های مشابه در دامنه های مختلف معنای متفاوتی داشته باشند. بنابراین، در DDD باید به جای داده، از مفهوم و منطق (logic) برنامه شروع کنیم.
- موجودیت ها، value object ها، repository ها و موارد دیگر تا زمانی که Ubiquitous language را تعریف نکنیم و محدوده ی مفاهیم (Bounded contexts) را تعیین نکنیم، معنایی ندارند. اگر ما شروع به کار با جزئیات پیاده سازی مانند این موارد کنیم، ممکن است به یک دامنه ی ضعیف برسیم که توسط تعداد زیادی سرویس و business logic احاطه شده است.
- بهتر است از مفاهیمی که عمومی و خاص برنامه نویسان هستند استفاده نکنیم. از لغاتی مانند Save، update، delete، handle، manage و غیره استفاده نکنیم. این مفاهیم بیش از حد فنی- انتزاعی هستند و معنای خاصی ندارند. درعوض، باید بر روی مفاهیم تجاری متمرکز باشیم و همیشه از دیدگاه متخصص کسب و کار فکر کنیم.
در DDD، موضوع Business transactions مهمتر از DB transactions هستند. DB transactions قابلیت ACID دارند و کاملاً سازگار و کوتاه مدت هستند در حالی که Business transactions اینگونه نیستند و ممکن است در نهایت سازگار (eventually consistent) شوند. در واقع، در جهان واقعی، ما در مورد DB transactions اطلاعاتی نداریم و فقط در مورد Business transactions اطلاعات داریم پس باید با این دیدگاه، اعمال و نتایج احتمالی آنها را بررسی کرده و مشخص کنیم چگونه می توان در صورت بروز خطا اقدامات را جبران کرد.
نتیجه گیری
مفاهیم مربوط به DDD که در این مقاله به آن پرداختیم برای آشنایی با این نوع رویکرد طراحی و معماری بود. مفهومی که دیدگاه طراحان معماری نرم افزار، توسعه دهندگان و حتی تست نویس ها را درباره ی توسعه ی نرم افزار تغییر داده است تا به این مفهوم یکپارچه با فضای کسب و کار برسیم که اول دامنه ی کاری را مشخص کنیم و سپس به زیر ساخت های آن فکر کنیم که این حتی در توسعه و مدیریت پروژه نیز بسیار کمک کننده است، زیرا مفاهیم مشهوری در حوزه ی فنی مانند single responsibility، loose coupling،high cohesion و isolating logic در آن تاکید شده است تا برنامه های ما سازگارتر، راحت تر و سریعتر شوند و با نیازهای فضای کسب و کار همسو شوند.
منابع
https://herbertograca.com/2017/09/07/domain-driven-design
https://www.infoq.com/articles/ddd-in-practice
https://dzone.com/articles/ddd-part-i-introduction