آشنایی با Domain-Driven Design (DDD): طراحی نرم‌افزار بر اساس مدل کسب‌وکار
ﺯﻣﺎﻥ ﻣﻄﺎﻟﻌﻪ: 11 دقیقه

آشنایی با Domain-Driven Design (DDD): طراحی نرم‌افزار بر اساس مدل کسب‌وکار

فرض کنید تیمی در حال توسعه یک سامانه فروش آنلاین است. مدیر محصول درباره «سفارش»، «مشتری» و «پرداخت» صحبت می‌کند، تحلیلگر کسب‌وکار هرکدام از این مفاهیم را به شکلی مستند کرده و برنامه‌نویسان هم آن‌ها را به کلاس‌ها و جداول دیتابیس تبدیل کرده‌اند. در نگاه اول، همه درباره یک موضوع صحبت می‌کنند، اما با گذشت زمان مشخص می‌شود که هر گروه برداشت متفاوتی از همین واژه‌های ساده دارد.

برای مثال، منظور واحد فروش از «مشتری» ممکن است هر فردی باشد که در سایت ثبت‌نام کرده است، در حالی که از دید واحد مالی، مشتری تنها کسی است که حداقل یک خرید موفق داشته باشد. اگر این تفاوت‌ها از ابتدا مشخص نشوند، خیلی زود منطق برنامه پیچیده، مبهم و پر از استثنا خواهد شد.

بسیاری از مشکلات پروژه‌های نرم‌افزاری نیز دقیقا از همین نقطه آغاز می‌شوند؛ نه از انتخاب زبان برنامه‌نویسی یا دیتابیس، بلکه از این‌که تیم توسعه مسئله کسب‌وکار را به‌درستی درک نکرده است. نتیجه چنین وضعیتی، کدی است که تغییر آن دشوار است، توسعه‌دهندگان برداشت‌های متفاوتی از مفاهیم دارند و هر قابلیت جدید، پیچیدگی بیشتری به سیستم اضافه می‌کند.

Domain-Driven Design (DDD) برای حل همین مسئله شکل گرفته است. این رویکرد تلاش می‌کند قبل از آن‌که به پیاده‌سازی فکر کنیم، درک مشترکی از مسئله ایجاد کنیم و ساختار نرم‌افزار را بر اساس مدل واقعی کسب‌وکار طراحی کنیم، نه صرفا بر اساس جداول دیتابیس یا ساختار کلاس‌ها.

قبل از DDD، باید Domain را بشناسیم

برای درک Domain-Driven Design، ابتدا باید بدانیم منظور از Domain چیست. در ساده‌ترین تعریف، Domain همان حوزه‌ای است که نرم‌افزار قرار است برای آن راه‌حل ارائه دهد، یعنی مجموعه‌ای از قوانین، مفاهیم، فرآیندها و اصطلاحاتی که یک کسب‌وکار یا سازمان با آن‌ها سروکار دارد.

برای مثال، اگر در حال توسعه یک سامانه بانکی باشید، مفاهیمی مانند حساب، تراکنش، وام و سود بخشی از Domain هستند. در یک فروشگاه اینترنتی، محصول، سبد خرید، سفارش و موجودی کالا Domain را شکل می‌دهند. یا در یک سامانه بیمارستانی، پرونده پزشکی، پزشک، نسخه و نوبت‌دهی عناصر اصلی Domain محسوب می‌شوند.

نکته مهم این است که Domain همان نرم‌افزار نیست. نرم‌افزار تنها ابزاری برای پیاده‌سازی قوانین و فرآیندهای Domain است. به همین دلیل، شناخت دقیق حوزه کسب‌وکار اهمیت بیشتری از انتخاب زبان برنامه‌نویسی یا فریمورک دارد.

به همین دلیل، توسعه‌دهندگان در DDD زمان قابل توجهی را صرف شناخت Domain می‌کنند. آن‌ها با کارشناسان کسب‌وکار، مدیران محصول و کاربران نهایی گفتگو می‌کنند تا بفهمند هر مفهوم چه معنایی دارد، چه قوانینی بر آن حاکم است و ارتباط آن با سایر بخش‌های سیستم چگونه است.

هرچه این شناخت عمیق‌تر باشد، احتمال اینکه ساختار نرم‌افزار با نیازهای واقعی کسب‌وکار همسو باشد بیشتر خواهد بود، موضوعی که هسته اصلی Domain-Driven Design را تشکیل می‌دهد.

ایده اصلی Domain-Driven Design چیست؟

اگر بخواهیم DDD را در یک جمله خلاصه کنیم، می‌توان گفت:

ابتدا مدل کسب‌وکار را به‌درستی درک کنید، سپس نرم‌افزار را بر اساس همان مدل طراحی کنید.

در بسیاری از پروژه‌ها، توسعه نرم‌افزار مستقیما با طراحی دیتابیس، ایجاد API یا نوشتن کلاس‌ها آغاز می‌شود. اما DDD پیشنهاد می‌کند قبل از هر تصمیم فنی، باید مطمئن شوید که تیم توسعه و متخصصان کسب‌وکار درک یکسانی از مسئله دارند.

به همین دلیل، در Domain-Driven Design تمرکز اصلی روی مدل‌سازی است، یعنی ساختن یک مدل مفهومی که نحوه عملکرد واقعی کسب‌وکار را نشان دهد. این مدل تنها یک نمودار یا مستند نیست، بلکه باید در ساختار کد، نام کلاس‌ها، سرویس‌ها و حتی دیتابیس نیز منعکس شود.

DDD بر سه اصل اساسی تاکید دارد:

  • درک عمیق مسئله: پیش از نوشتن کد، باید قوانین، فرآیندها و مفاهیم اصلی کسب‌وکار به‌خوبی شناخته شوند.
  • ایجاد یک مدل مشترک: همه اعضای تیم، از توسعه‌دهندگان گرفته تا کارشناسان کسب‌وکار، باید از مفاهیم و اصطلاحات یکسانی استفاده کنند.
  • بازتاب مدل در کد: ساختار نرم‌افزار باید تا حد امکان با مدل کسب‌وکار همخوانی داشته باشد، نه اینکه صرفا بر اساس ملاحظات فنی طراحی شود.

برای مثال، اگر در مدل کسب‌وکار «سفارش» مهم‌ترین مفهوم سیستم است، این اهمیت باید در کد نیز دیده شود. کلاس‌ها، سرویس‌ها و قوانین مرتبط با سفارش باید همان ساختاری را داشته باشند که در دنیای واقعی کسب‌وکار وجود دارد، نه اینکه بین بخش‌های مختلف پروژه پراکنده شوند.

همه باید به یک زبان صحبت کنند

یکی از مهم‌ترین ایده‌های Domain-Driven Design مفهومی به نام Ubiquitous Language یا «زبان مشترک» است. منظور از زبان مشترک این است که تمام اعضای تیم، از مدیر محصول و تحلیلگر کسب‌وکار گرفته تا برنامه‌نویس و تستر، برای اشاره به مفاهیم سیستم از واژه‌های یکسانی استفاده کنند.

شاید این موضوع در ابتدا ساده به نظر برسد، اما در پروژه‌های واقعی یکی از رایج‌ترین دلایل سوءتفاهم است.

فرض کنید در یک فروشگاه اینترنتی، کارشناسان کسب‌وکار از واژه Order برای سفارش استفاده می‌کنند. اگر توسعه‌دهندگان در کد از نام‌هایی مانند PurchaseRequest یا Transaction برای همین مفهوم استفاده کنند، به‌مرور زمان فاصله‌ای بین مدل ذهنی کسب‌وکار و ساختار نرم‌افزار ایجاد می‌شود. نتیجه این اختلاف، جلسات طولانی، برداشت‌های متفاوت و در نهایت کدی است که درک آن دشوار خواهد بود.

در DDD تلاش می‌شود همان اصطلاحاتی که در جلسات و مستندات کسب‌وکار به کار می‌روند، در کد نیز دیده شوند. اگر همه درباره «سفارش»، «مشتری» یا «فاکتور» صحبت می‌کنند، بهتر است همین مفاهیم در نام کلاس‌ها، متدها، سرویس‌ها و مستندات نیز استفاده شوند.

برای مثال، اگر در جلسات گفته می‌شود:

«هر سفارش باید قبل از ارسال، پرداخت شده باشد.»

بهتر است این منطق در کد نیز به همین شکل قابل تشخیص باشد، نه اینکه در میان چندین کلاس و متد با نام‌های نامرتبط پنهان شود.

وقتی همه اعضای تیم از یک زبان مشترک استفاده می‌کنند، ارتباط بین افراد ساده‌تر می‌شود، احتمال سو برداشت کاهش می‌یابد و کد نیز برای اعضای جدید تیم بسیار قابل‌فهم‌تر خواهد بود. به همین دلیل، Ubiquitous Language یکی از پایه‌های اصلی Domain-Driven Design محسوب می‌شود.

اجزای اصلی Domain-Driven Design را بشناسیم

تا اینجا با ایده‌های اصلی DDD آشنا شدیم. اما برای پیاده‌سازی این رویکرد، مجموعه‌ای از مفاهیم و الگوها نیز معرفی شده‌اند که به آن‌ها Building Blocks گفته می‌شود. این اجزا به توسعه‌دهندگان کمک می‌کنند مدل کسب‌وکار را به شکلی منظم در کد پیاده‌سازی کنند.

Entity

Entity شیئی است که هویت منحصربه‌فرد دارد و حتی اگر ویژگی‌های آن تغییر کند، همچنان همان شئ محسوب می‌شود. برای مثال، یک «سفارش» با شناسه OrderId را در نظر بگیرید. ممکن است وضعیت سفارش از «در انتظار پرداخت» به «ارسال شده» تغییر کند، اما همچنان همان سفارش است و هویت آن تغییر نمی‌کند.

Value Object

برخلاف Entity، یک Value Object هویت مستقل ندارد و تنها با مقدارش شناخته می‌شود. برای مثال، «آدرس» یا «محدوده زمانی» Value Object هستند. اگر دو آدرس تمام جزئیات یکسانی داشته باشند، از دید سیستم تفاوتی میان آن‌ها وجود ندارد.

Aggregate

در بسیاری از موارد، چند Entity و Value Object به هم وابسته هستند و باید به‌عنوان یک مجموعه مدیریت شوند. به این مجموعه Aggregate گفته می‌شود. برای مثال، یک سفارش می‌تواند شامل چندین قلم کالا، اطلاعات پرداخت و آدرس ارسال باشد. DDD پیشنهاد می‌کند این اجزا از طریق یک نقطه مرکزی مدیریت شوند تا قوانین کسب‌وکار همیشه به‌درستی اجرا شوند.

Repository

Repository مسئول ذخیره و بازیابی Aggregateها از منبع داده است. نکته مهم این است که Repository نباید محل پیاده‌سازی منطق کسب‌وکار باشد. وظیفه آن تنها فراهم کردن راهی برای دسترسی به داده‌ها است تا سایر بخش‌های سیستم نیازی به دانستن جزئیات پایگاه داده نداشته باشند.

Domain Service

گاهی یک قانون کسب‌وکار به یک Entity خاص تعلق ندارد و بین چند مفهوم مختلف مشترک است. در این شرایط از Domain Service استفاده می‌شود. برای مثال، محاسبه هزینه ارسال سفارش ممکن است به وزن کالا، مقصد، نوع ارسال و قوانین شرکت حمل‌ونقل وابسته باشد. چنین منطقی در یک Domain Service قرار می‌گیرد.

Factory

گاهی ایجاد یک شئ جدید ساده نیست و برای ساخت آن باید چندین قانون کسب‌وکار رعایت شود. در این شرایط، Factory مسئول ایجاد آن شئ خواهد بود. این کار باعث می‌شود منطق ایجاد اشیا در یک محل متمرکز شود و کلاس‌های دیگر مسئولیت اضافی نداشته باشند.

آیا Domain-Driven Design برای همه پروژه‌ها مناسب است؟

یکی از رایج‌ترین اشتباهات این است که تصور کنیم DDD بهترین انتخاب برای هر پروژه نرم‌افزاری است. در حالی که این رویکرد برای حل نوع خاصی از مسائل طراحی شده و استفاده از آن در پروژه‌های ساده، گاهی فقط پیچیدگی غیرضروری ایجاد می‌کند.

اگر در حال توسعه یک وب‌سایت شخصی، یک پنل مدیریت ساده یا یک سیستم CRUD هستید که بیشتر عملیات آن ایجاد، نمایش، ویرایش و حذف داده‌هاست، احتمالا نیازی به Domain-Driven Design نخواهید داشت. در چنین پروژه‌هایی، هزینه طراحی مدل‌های پیچیده و مفاهیم DDD بیشتر از مزایای آن است.

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

نمونه‌هایی از این نوع پروژه‌ها عبارت‌اند از:

  • سامانه‌های بانکی و مالی
  • سیستم‌های بیمه
  • نرم‌افزارهای ERP
  • پلتفرم‌های تجارت الکترونیک بزرگ
  • سامانه‌های لجستیک و زنجیره تأمین
  • سیستم‌های رزرو و مدیریت خدمات

در چنین پروژه‌هایی، درک دقیق مفاهیم کسب‌وکار و طراحی نرم‌افزار بر اساس آن‌ها، در بلندمدت باعث می‌شود توسعه قابلیت‌های جدید، نگهداری کد و همکاری بین اعضای تیم ساده‌تر شود. بنابراین، DDD را نباید یک نسخه عمومی برای همه پروژه‌ها دانست.

چند تصور اشتباه درباره Domain-Driven Design

از آنجا که DDD مفاهیم متعددی را در بر می‌گیرد، گاهی برداشت‌های نادرستی درباره آن شکل می‌گیرد. در ادامه، به چند مورد از رایج‌ترین این تصورات اشاره می‌کن‌کنیم.

«DDD فقط مجموعه‌ای از الگوهای طراحی است.»

الگوهایی مانند Entity ،Repository یا Aggregate بخشی از DDD هستند، اما تمام آن نیستند. هسته اصلی DDD، درک صحیح دامنه کسب‌وکار و تبدیل آن به یک مدل نرم‌افزاری است. اگر این مدل وجود نداشته باشد، استفاده از این الگوها به‌تنهایی ارزش چندانی ایجاد نمی‌کند.

«DDD فقط برای معماری Microservices است.»

اگرچه بسیاری از پروژه‌های مبتنی بر Microservices از مفاهیم DDD مانند Bounded Context استفاده می‌کنند، اما DDD سال‌ها قبل از رواج میکروسرویس‌ها معرفی شده بود. این رویکرد را می‌توان در یک برنامه یکپارچه (Monolith) نیز با موفقیت به کار گرفت.

«DDD یعنی استفاده از Repository و پوشه‌های زیاد.»

برخی تصور می‌کنند با ایجاد پوشه‌هایی مانند Domain ،Application و Infrastructure یا پیاده‌سازی چند Repository، پروژه آن‌ها بر اساس DDD طراحی شده است. در واقع، این‌ها تنها بخشی از ساختار فنی هستند. اگر مدل نرم‌افزار بازتاب‌دهنده مفاهیم واقعی کسب‌وکار نباشد، صرفا تغییر ساختار پوشه‌ها به معنای استفاده از DDD نیست.

«DDD فقط برای Java و .NET کاربرد دارد.»

اگرچه بسیاری از نمونه‌ها و کتاب‌های DDD با زبان‌هایی مانند Java یا سی‌شارپ نوشته شده‌اند، اما این رویکرد به زبان برنامه‌نویسی وابسته نیست. می‌توان اصول Domain-Driven Design را در پروژه‌هایی با Python، گولنگ، کاتلین، جاوااسکریپت، Rust و بسیاری از زبان‌های دیگر نیز به کار گرفت.

«DDD همه مشکلات پروژه را حل می‌کند.»

هیچ رویکردی برای همه پروژه‌ها مناسب نیست و DDD نیز از این قاعده مستثنا نیست. اگر دامنه کسب‌وکار ساده باشد، استفاده از DDD ممکن است تنها پیچیدگی بیشتری به پروژه اضافه کند. DDD زمانی بیشترین ارزش را ایجاد می‌کند که مسئله کسب‌وکار پیچیده باشد و تیم توسعه نیاز داشته باشد ساختار نرم‌افزار را با مفاهیم واقعی آن کسب‌وکار همسو کند.

جمع‌بندی

Domain-Driven Design بیش از آنکه یک الگوی طراحی یا معماری باشد، رویکردی برای درک بهتر مسئله و طراحی نرم‌افزار بر اساس واقعیت‌های کسب‌وکار است. این رویکرد با ایجاد زبان مشترک، مدل‌سازی دقیق Domain و تفکیک مسئولیت‌ها، به تیم‌های توسعه کمک می‌کند سیستم‌هایی بسازند که در بلندمدت نگهداری و توسعه آن‌ها آسان‌تر باشد.

البته DDD برای همه پروژه‌ها مناسب نیست. اگر با یک دامنه کسب‌وکار پیچیده سروکار دارید، می‌تواند ارزش زیادی ایجاد کند؛ اما برای پروژه‌های کوچک و ساده، راهکارهای سبک‌تر معمولاً انتخاب منطقی‌تری هستند.

چه امتیازی برای این مقاله میدهید؟

خیلی بد
بد
متوسط
خوب
عالی
در انتظار ثبت رای

/@arastoo
ارسطو عباسی
کارشناس تست نرم‌افزار و مستندات

...

دیدگاه و پرسش
برای ارسال دیدگاه لازم است وارد شده یا ثبت‌نام کنید ورود یا ثبت‌نام

در حال دریافت نظرات از سرور، لطفا منتظر بمانید

در حال دریافت نظرات از سرور، لطفا منتظر بمانید

ارسطو عباسی

کارشناس تست نرم‌افزار و مستندات