Mohsen Farokhi - محسن فرخی
Mohsen Farokhi - محسن فرخی
خواندن ۱۱ دقیقه·۲ سال پیش

Domain Driven Design - بخش دوم

در این بخش به بررسی انواع مختلفی که یک core domain می تواند داشته باشد و موضوعات حول محور آن می پردازیم.

Domain Driven Design
Domain Driven Design

ممکن است در یک سیستم، بیش از یک subdomain به عنوان core domain داشته باشیم. اما تاکید می شود که قسمت اصلی سیستم شناسایی شود و به عنوان یک core domain در نظر گرفته شود. در نظر داشته باشید که core domain بخش کوچکی از پروژه را تشکیل می دهد.

موضوعی که توسط آقای Eric Evans در راستای قانون 80-20 مطرح می شود، این است که core domain بیست درصد از use caseهای کاربر را شامل می شود در حالی که هشتاد درصد effort را ممکن است از شما بگیرد.

بنابراین قانون 80-20 یا Pareto در software design، مطرح می کند که هشتاد درصد از نیازهای کاربر و business value، از طریق بیست درصد use caseها بدست می آید. شناسایی این use caseها، مستقیما روی معماری سیستم تاثیر گذار هستند.


در strategic design، با در نظر گرفتن نمودار زیر، اگر محور y را پیچیدگی در نظر بگیریم و محور x مزیت رقابتی ما در بازار باشد، هر چقدر x بزرگتر باشد موفقیت بیشتری برای business ما در بازار به همراه خواهد داشت.
بنابراین core domain از لحاظ business differentiation، در بالای محور x قرار دارد. در مورد generic domains و supporting domains، این موضوع اهمیت کمتری دارد. درصد کمتری از supporting domainها هستند که در بازار می تونن مزیت رقابتی ایجاد کنند.
یک business، دارای featureهایی است که آنها را تحت عنوان mission critical می شناسیم. وجود آنها الزامی است و فعالیت business ما به آنها وابسته است. این featureها عمدتا از سطح پیچیدگی بالایی برخوردار هستند و در core domain قرار دارند.


در طول توسعه سیستم ها، یکسری از الگوهای حرکتی را در subdomainها می بینیم. به این معنی که در طول زمان، subdomainهای ما می توانند دچار تغییرات شوند و نگرش ما ممکن است به خیلی از موارد تغییر کند.

حالت هایی در پروژه داریم که در طول زمان ممکن است اتفاق بیافتند. برای مثال، موضوع Short-Term Core Domain یکسری از نیازمندی ها و فعالیت هایی است که سازمان به صورت کوتاه مدت مدنظر دارد تا بتواند در بازار فعال شود. این موضوع می تواند دلایل مختلفی داشته باشد. فرض کنید یک سازمان بر روی این تمرکز کرده است که یک سیستم تخفیف دهی خوب داشته باشد تا بتواند بازار را جذب خودش کند. این ممکن است دلیلی باشد که ما core domainهای کوتاه مدت داشته باشیم.

موضوع دیگری که می تواند اتفاق بیافتد، فراگیر شدن یک feature و از دست دادن مزیت رقابتی آن است.

در طول زمان، ممکن است به این نتیجه برسیم که بعضی از subdomainهایی که کمتر به آنها فکر کرده بودیم، نتیجه مثبتی در پروژه ما داشته اند. بنابراین Hidden Core Domains از subdomainهایی هستند که ما فکر می کردیم supporting domain هستند اما ممکن به خاطر اشتباه تحلیلی یا نیاز بازار اتفاق افتاده باشند.

ممکن است زمانی با یک feature جدید وارد بازار شده باشیم و بعد از مدتی اکثر رقیب ها، آن feature را داشته باشند. بنابراین تغییر استراژی در اینجا اهمیت پیدا می کند که از بین subdomainهایی که داریم کدامیک می تواند برای ما مزیت رقابتی ایجاد کند.

ممکن است یک feature که برای ما مزیت رقابتی محسوب می شده، توسط سازمان دیگری فراگیر شود. برای مثال یک محصول open source که فراگیر می شود و ممکن است به دلیل استفاده زیاد از آن، خود ما هم بخواهیم از آن استفاده کنیم.


Bounded Context

در برخی موارد، ممکن است subdomain و bounded context، اشتباه گرفته شوند.

مواردی که تا این لحظه به آن ها پرداخته شد، همه در حوزه Problem Space یا فضا و شناخت مسئله قرار می گیرند. به این معنی که ما هنوز وارد فضای راه حل نشده ایم.

تصویر بالا، problem space را نشان می دهد. تیم توسعه و business expertها از طریق فرآیندهای knowledge crunching و ubiquitous language که DDD بر روی آن تاکید می کند، knowledge کلی در یک نرم افزار را استخراج می کنند. این knowledge به حوزه های کاری مختلف شکسته می شود. بعد از آن، روی آن ها ارزش گذاری می شود.

موضوع در Solution Space یا فضای راه حل ممکن است یک مقدار متفاوت باشد. مفهوم Bounded Context، یک پروژه فیزیکی است که درون آن کد قرار دارد و در دنیای واقعی پیاده سازی می شود.

در حالت ایده آل، هر subdomain در یک bounded context پیاده سازی می شود. اما گاهی موضوع به این شکل نیست و ممکن است این دو مفهوم الزاما یک به یک نباشند.

یکی از دلایل آن می تواند وجود سیستم های قدیمی و موجود یا legacy باشد. فرض کنید در پیاده سازی یک سامانه تدارکات، subdomainهای procurement و inventory و warehouse در فضای مساله شناسایی شده اند. در ارائه راه حل، مشاهده می شود که موضوع متفاوت است. آن سازمان یک سامانه قدیمی دارد که کنترل موجودی و قسمتی از انبار در آن پیاده سازی شده است و ما نمی توانیم آن را کنار بگذاریم.

مفهوم bounded context یا BC، یک پروژه نرم افزاری است که boundary یا مرز مشخصی دارد. در حالت ایده architecture و team مربوط به خودش را دارد و ممکن است language مربوط به خودش را نیز داشته باشد و به عنوان یک واحد autonomous یا مستقل در پروژه ما عمل می کند.


معیارهای تشخیص bounded context

مهمترین موضوعی که در شناسایی BCها با آن روبرو هستیم، موضوع language است. جایی که احساس که کنیم که context زبان تغییر می کند و داریم وارد یک حوزه dictionary دیگری می شویم، آنجا دو مرز مشخص را می بینیم. این موضوع می تواند از لحاظ واژگان مختلف خودش را نشان دهد.

همچنین از لحاظ perspective یا چشم انداز نیز می توانیم تغییرات را احساس کنیم. به این معنی که در دنیای واقعی مفهومی وجود دارد که در یک حوزه به یک اسم شناسایی می شود و در حوزه دیگر به اسم دیگر. برای مثال محصول و مرسوله از هم مرز متفاوتی دارند.

وجود domain expertهای متفاوت نیز می تواند نشان دهنده مرزهای متفاوتی باشد.

در قانون Conway در این موضوع مطرح می شود که ارتباط هایی که در یک سازمان وجود دارد، می تواند در design نرم افزار به صورت ناخودآگاه تاثیر داشته باشد.

حوزه فعالیت بر روی بخش هایی از سیستم که بر روی هم cohesion دارند و به هم مرتبط هستند، می توانند در شناسایی مرزها تاثیر گذار باشند.

بخش های مرتبط با موارد فنی مثل Transaction Boundary و داده های مرتبط در نحوه مرز بندی BCها می تواند تاثیر گذار باشد.


Context Mapping

یکی از موضوع هایی که در strategic design داریم، context mapping است. بخش های متفاوت یک نرم افزار برای رسیدن به هدف آن نرم افزار با هم در رابطه هستند و کار می کنند. بنابراین موضوع مهمی که وجود دارد ارتباط بین BCهایی است که ما طراحی کرده ایم. انواع رابطه بین BCها وجود دارد. در DDD نهایتا به یک context map می رسیم که BCها روی آن ترسیم شده اند و نحوه رابطه بین آن ها مشخص شده است.

اولین موضوع که در جریان context mapping وجود دارد، تفاوت های linguistic بین سیستم ها می باشد. فرض کنید در یک BC به نام Product Management، یک مفهوم به نام product وجود دارد. از طرفی یک BC به نام Inventory داریم و در آن مفهومی به نام stock وجود دارد. مفهوم product و stock، از لحاظ language و ساختاری متفاوت هستند.

در جایی از پروژه نیاز داریم که از inventory یک فراخوانی به product management انجام شود و لیست کالاها را دریافت می کنیم. اتفاقی که می افتد، وقتی API مربوط به سیستم product management فراخوانی می شود و داده ها دریافت می شوند، یک product را دریافت می کنیم که از لحاظ linguistic برای ما شناخته شده نیست. بنابراین اتفاق بدی که می افتد این است که در domain model طراحی شده در inventory، مفاهیم و واژگانی از یک BC دیگر رخنه پیدا می کنند.

Anti-Corruption Layer

اولین بحثی که در موضوع ارتباط بین BCها مطرح می شود، مفهوم Anti-Corruption Layer یا ACL است. ACL جهت حفاظت از رخنه کردن مفاهیم domainهای مختلف داخل همدیگر مورد استفاد قرار می گیرد. به این صورت که ACL به عنوان یک واحد translation وارد عمل می شود. وقتی که product می خواهد وارد سیستم inventory شود، وارد یک ACL می شود. وظیفه ACL، تبدیل مفاهیم BCها است.

Shared Kernel

موضوع دیگری که در رابطه بین BCها وجود دارد، shared kernel است که باید با دقت از آن استفاده شود. زمانی که در حال مدل کردن دو بخش مختلف هستیم، ممکن است با مدل هایی مواجه شویم که بین BCها دارای اشتراک هستند. برای مثال در HR context و Payroll context، یک مدل به نام employee به اشتراک وجود دارد. در واقع رویکرد shared kernel به این شکل است که آن مدل را بین دو BC به اشتراک می گذارد و در هردوی آن ها مورد استفاده قرار می گیرد.

موضوع مهمی که در این حوزه وجود دارد این است که shared kernel، از آنجایی که یک مدل مشترک بین دو BC می سازد، در خیلی از مواقع ممکن است هزینه هایی را برای ما به همراه داشته باشد. برای مثال وقتی که دو تیم مختلف وجود دارند که در یک ارتباط نزدیک بر روی دو BC کار می کنند و مشترکات زیادی در حوزه domain و logic دارند، می توانند از یک مدل اشتراکی استفاده کنند تا هزینه های translation و ACL را کاهش دهند.

اما نکته ای که وجود دارد، به محض استفاده از shared kernel، یک code dependency ایجاد می شود. وجود code dependency باعث تحمیل هزینه زیاد integration به تیم ها می شود. به این معنی که، وقتی داخل مدل را بخواهیم تغییر دهیم باید مراقب نیازهای تیم دیگر هم باشیم و کدهای آن ها از کار نیافتد.

Open Host Service

ممکن است به مساله ای برسیم که یک context داریم و contextهای دیگر از آن دیتا می گیرند و هر کدوم یک ACL دارند که با تفاوت خیلی کمی، داده ها را به داده هایی که می خواهند تبدیل می کنند.

در open host service، موضوع می تواند متفاوت مطرح شود و رابطه برعکس شود. به این صورت که در سطح ارائه سرویس، translation انجام می شود. به این ترتیب بار translation را از روی گیرنده ها به سمت فرستنده انتقال پیدا می کند.

Separate Ways

ممکن است به عنوان طراح سیستم به مساله ای برسیم که دو context نیاز شبیه به هم دارند. اما بر اساس اتفاقاتی که ممکن است در سازمان وجود داشته باشد، هزینه integration را بیشتر از هزینه جدا نوشتن تلقی می کنیم. برای مثال تیم هایی وجود دارند که ممکن است همکاری خوبی با هم نداشته باشند. بنابراین در separate way با وجود اینکه می توانیم مسئله مشترک را integration کنیم و یکبار آن را بنویسیم، تصمیم می گیریم که به دلیل هزینه های سرباری که ممکن است در این جریان اتفاق بیافتد، این کار را نکنیم.

Partnership

در نوع رابطه partnership، تیم ها در contextهای مختلف هستند اما به سمت یک هدف مشترک حرکت می کنند. برای مثال دو context تخفیف و فروش، یک حوزه را پوشش می دهند. این مساله که در فضای partnership بدونیم که دو BC رابطه همکاری دارند، از لحاظ strategic برای ما اهمیت دارد. ازین لحاظ که در چینش افراد اهمیت پیدا می کند. در فضای shared kernel، ارتباط تیم ها حتما باید partnership باشد. بنابراین هدف این است که دو تیم که در contextهای مختلف کار می کنند با هم رابطه همکاری داشته باشند.

Conformist

به مدلی اشاره می کند که در ارتباط بین دو تیم، یک تیم از تیم دیگر تبعیت می کند. به این معنی که یک تیم در یک context سرویس را ارائه می کند و تیم ها در contextهای دیگر باید نیاز خودشان را با آن تطبیق دهند. این موضوع گاهی به شکل اجباری اتفاق می افتد. برای مثال کار کردن با یک سرویس IPG، یک رابطه conformist است. و گاهی می تواند انتخابی باشد. برای مثال، یک تیم بر روی core domain کار می کند و به عنوان معمار پروژه، تعیین می کنیم که تیم های دیگر برای تطبیق نیازهای خود، ACL بنویسند.

Upstream/Downstream

در رابطه بین دو تیم، به تیمی که به داده ها یا سرویس های تیم دیگر وابسته باشد downstream می گویند و به تیمی که این داده ها و سرویس ها را ارائه می کند upstream گفته می شود.

Customer-Supplier

در این رابطه، تیم upstream در تصمیمات و رویکردهای خود، تیم downstream را لحاظ می کند. برای مثال، افرادی هستند که بر روی core domain کار می کنند و از یک BC دیگر یک سرویس می خواهند. اگر اینجا رابطه customer-supplier نباشد، رابطه conformist پیش می آید. در رابطه customer-supplier، تیمی که supplier یا تامین کننده است، در تصمیماتی که برای سرویس یا طراحی می گیرد، تیم گیرنده را لحاظ می کند. به این معنی که تیم دریافت کننده باید در طراحی خود این موارد را ببیند.

Bubble Context

زمانی که وارد توسعه یک سیستم می شویم، ممکن است با نرم افزارهای legacy روبرو شویم. در مدل legacy، ممکن است model و dictionary مناسبی نبینیم.

مفهوم bubble context، یکی از روش هایی است که در DDD برای کار با سیستم های legacy استفاده می شود. زمانی که یک سیستم legacy داریم و می خواهیم از آن سرویس بگیریم، bubble context از روی سرویس های legacy، یکسری API استاندارد و قابل استفاده درست می کند و expose می کند.

domain driven design
شاید از این پست‌ها خوشتان بیاید