ویرگول
ورودثبت نام
Amin Sotoudeh
Amin Sotoudehاز کد زدن تا تحلیلگر سیستم و کسب‌وکار؛ پنج ساله پل بین تیم فنی و بیزینس هستم. و اینجا از تجربه‌هایم در مدیریت محصول می‌نویسم.
Amin Sotoudeh
Amin Sotoudeh
خواندن ۴ دقیقه·۱۲ روز پیش

طراحی دامنه-محور (DDD): قطب‌نمای مهندسی در دلِ پیچیدگی‌های نرم‌افزار

سلام دوستان و همکاران گرامی.

آیا تا به حال پروژه‌ای داشته‌اید که از نظر تکنیکال شاهکار باشد، معماری تمیز، کدنویسی مدرن و زیرساخت ابری فوق‌العاده، اما در نهایت مشتری از آن راضی نباشد؟ یا بدتر از آن، سیستم نتواند نیازهای واقعی بیزینس را برآورده کند؟

استیو جابس می‌گوید: “این وظیفه مشتری نیست که بداند چه می‌خواهد.” اما وظیفه ماست که آن نیاز پنهان را کشف و به کد تبدیل کنیم.

امروز می‌خواهیم درباره Domain-Driven Design (DDD) یا طراحی دامنه-محور صحبت کنیم. رویکردی که اریک ایوانز (Eric Evans) در سال ۲۰۰۴ معرفی کرد تا تمرکز ما را از “تکنولوژی صرف” به “قلب کسب‌وکار” برگرداند.


DDD چیست؟

بیایید این عبارت را کلمه به کلمه باز کنیم:

  1. دامنه (Domain): قلمرو یا موضوع خاصی که نرم‌افزار قرار است مشکل آن را حل کند. مثلاً در یک نرم‌افزار بانکی، دامنه شامل مفاهیمی مثل “حساب”، “تراکنش” و “قوانین بانکی” است.

  2. محور/رانده شده (Driven): یعنی طراحی سیستم ما تحت تأثیر و هدایتِ نیازهای دامنه است، نه صرفاً دیتابیس یا فریم‌ورک UI.

  3. طراحی (Design): نقشه راه و بلوپرینتی که مشخص می‌کند اجزای سیستم چطور برای حل مشکلات آن دامنه کنار هم قرار می‌گیرند.

به زبان ساده: DDD یعنی درک عمیق کسب‌وکار و مدل‌سازی آن در کد، به طوری که نرم‌افزار آینه‌ای تمام‌نما از واقعیت بیزینس باشد.


دو بالِ پرواز در DDD: استراتژیک و تاکتیکال

در DDD ما با دو سطح از طراحی روبرو هستیم. سطح کلان (Strategic) و سطح پیاده‌سازی (Tactical).

۱. طراحی استراتژیک (Strategic Design)

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

  • زبان مشترک (Ubiquitous Language):

شاید مهمترین اصل DDD همین باشد. توسعه‌دهندگان و متخصصان کسب‌وکار (Domain Experts) باید از یک واژگان مشترک استفاده کنند. نباید در جلسه با بیزینس بگوییم “Routing Logic” و آن‌ها بگویند “مسیر سفر”. همه باید از کلمه “Ride Route” استفاده کنند. این کار ابهام را از بین می‌برد.

  • قلمرو معنایی (Bounded Contexts):

یک کلمه می‌تواند در بخش‌های مختلف معانی متفاوتی داشته باشد. مثلاً کلمه “مشتری” در بخش فروش معنایی متفاوت با بخش پشتیبانی دارد. ما سیستم را به “قلمرو های” مشخص تقسیم می‌کنیم تا هر مدل در قلمرو خودش معنای دقیق داشته باشد و تداخل ایجاد نشود.

  • نگاشت بافت‌ها (Context Mapping):

اینکه این بافت‌های جداگانه چطور با هم حرف می‌زنند (مثلاً از طریق Shared Kernel یا لایه ضد-فساد ACL).

  • Anti-Corruption Layer (ACL): لایه‌ای که از سیستم ما در برابر مدل‌های قدیمی یا خارجی محافظت می‌کند و داده‌ها را ترجمه می‌کند تا مدل تمیزِ دامنه ما آلوده نشود.

۲. الگوهای تاکتیکال (Tactical Patterns)

حالا که معماری کلان را داریم، چطور آن را کدنویسی کنیم؟ ابزارهای ما این‌ها هستند:

  • Entity (موجودیت): اشیایی که “هویت” (Identity) دارند و در طول زمان تغییر می‌کنند. (مثال: یک User که ایمیلش عوض می‌شود اما همان آدم است).

  • Value Object (شیء ارزشی): اشیایی که هویت ندارند و با مقدارشان شناخته می‌شوند. تغییر ناپذیرند. (مثال: Money یا Location. اسکناس ۱۰ هزار تومانی، ۱۰ هزار تومان است، مهم نیست کدام اسکناس باشد).

  • Aggregate (مجموعه): خوشه‌ای از اشیاء که باید با هم سازگار باشند. یک Entity اصلی به نام Aggregate Root دارد. (مثال: Order و OrderItems. شما نمی‌توانید بدون سفارش، آیتم سفارش داشته باشید).

  • Repository: رابطی برای دسترسی به داده‌ها (مثل دیتابیس) که پیچیدگی‌های فنی ذخیره‌سازی را از دید دامنه پنهان می‌کند.

  • Service: وقتی یک عملیات به یک Entity یا Value Object خاص تعلق ندارد، آن را در یک سرویس دامنه (Domain Service) قرار می‌دهیم.

  • Factory: مسئول ساختن اشیاء پیچیده است تا کدِ ساختن (Creation logic) در بیزینس پخش نشود.


مثال دنیای واقعی: اپلیکیشن تاکسی آنلاین (RideX)

بیایید با یک مثال ملموس همه چیز را جمع‌بندی کنیم. فرض کنید در حال ساخت RideX (شبیه اسنپ یا تپسی) هستیم.

۱. زبان مشترک:

  • مسافر (User): کسی که درخواست می‌دهد.

  • سفر (Ride): نمونه‌ای از جابجایی با قیمت و زمان مشخص.

۲. قلمرو معنایی (Bounded Contexts):

  • مدیریت سفر: درخواست سفر، پیدا کردن راننده.

  • مالی: پرداخت‌ها، کیف پول راننده.

۳. پیاده‌سازی تاکتیکال:

  • Entities:

  • Driver (با ID راننده، وضعیت خودرو).

  • Ride (با ID سفر، وضعیت: در‌حال‌انجام/تمام‌شده).

  • Value Objects:

  • Location (شامل طول و عرض جغرافیایی). تغییر این مختصات یعنی تغییر کل لوکیشن.

  • Aggregates:

  • Ride Aggregate: ریشه آن Ride است که User و Driver و Location را مدیریت می‌کند. هر تغییری در وضعیت سفر باید از طریق Ride انجام شود تا سازگاری داده‌ها حفظ شود.

  • Services:

  • RideAssignmentService: منطق پیچیده پیدا کردن نزدیکترین راننده به مسافر.


چرا باید (و نباید) از DDD استفاده کنیم؟

مزایا:

  • ارتباط موثر: هم‌زبانی تیم فنی و بیزینس.

  • انعطاف‌پذیری: چون منطق بیزینس از زیرساخت جداست، تغییرات راحت‌تر است.

  • کیفیت نرم‌افزار: کدی که دقیقاً کاری را می‌کند که بیزینس می‌خواهد.

چالش‌ها:

  • پیچیدگی: برای پروژه‌های کوچک (CRUD ساده) مثل کشتن پشه با تانک است!

  • منحنی یادگیری: یادگیری و تغییر طرز تفکر تیم زمان‌بر است.

  • نیاز به تعامل بالا: نیاز به دسترسی مداوم به متخصصان دامنه (Domain Experts) دارد.


کلام آخر

DDD فقط مجموعه‌ای از الگوها مثل Repository یا Factory نیست؛ DDD یک طرز تفکر است. تفکری که می‌گوید قلب نرم‌افزار، تکنولوژی نیست، بلکه مشکلی است که قرار است برای انسان‌ها حل کند.

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

آیا تجربه استفاده از DDD را در پروژه‌هایتان داشته‌اید؟ چالش اصلی شما چه بود؟ در بخش نظرات بنویسید.

محمد امین ستوده

dddنرم‌افزارارتباط موثرتیم فنیمعماری نرم افزار
۲
۰
Amin Sotoudeh
Amin Sotoudeh
از کد زدن تا تحلیلگر سیستم و کسب‌وکار؛ پنج ساله پل بین تیم فنی و بیزینس هستم. و اینجا از تجربه‌هایم در مدیریت محصول می‌نویسم.
شاید از این پست‌ها خوشتان بیاید