شما اینجا هستید: خانه / مقالات آموزشی / آشنایی با مفاهیم Domain-Driven Design – بخش دوم

آشنایی با مفاهیم Domain-Driven Design – بخش دوم

در مقدمه با کلیات رویکرد DDD برای توسعه ی نرم افزار و در بخش اول از این سری مقالات با مفاهیم Entity، ValueObject و Service آشنا شدیم.  در این مقاله قصد داریم تا به بررسی مفهوم کلیدی و اساسی Aggregate بپردازیم. اما قبل از پرداختن به این دو مفهوم لازم است تا با مفهوم دیگر به نام به نام Invariant آشنا شویم :

  • Invariant چیست؟

Invariant (به فارسی : ثابت، نامتغیر) Business Rule یا قانونی از دومین است که همیشه باید رعایت شود. در صورتی که Invariant های یک شیء رعایت نشوند، آن شیء در وضعیت نامعتبر (Invalid) قرار میگیرد. برای درک بهتر به مثال ساده ی زیر توجه کنید :

order-orderitem

هر سفارش (Order) از تعدادی جزء سفارش (OrderItem) تشکیل شده است و هر جزء سفارش یک قیمت (Price) مشخص دارد. قیمت واقعی یک سفارش از مجموع قیمت اجزاء آن محاسبه می شود. حال فرض کنید قیمت یک سفارش هرگز نباید بیشتر از N شود. این شرط یک Invariant بوده و در صورتی که صحیح نباشد، شیء سفارش در وضعیت نامعتبر قرار میگرد.

  • تعریف Aggregate

هنگامی که مدل نرم افزار پیچیده و رابطه ی بین کلاس ها زیاد می شود، کنترل تغییرات بر روی اشیاء و اطمینان از صحت Invariant ها مشکل می شود زیرا اغلب اوقات کنترل Invariant ها بین مجموعه ای از اشیاء صورت می گیرد. برای حل مشکلات این چنینی مفهومی به نام Aggregate ایجاد شده است که Eric Evans در کتاب Domain-Driven Design آن را اینگونه تعریف میکند :

Aggregate به مجموعه ای از اشیاء مرتبط گفته می شود که جهت کنترل تغییرات، به عنوان “یک واحد” در نظر گرفته می شوند.

هر Aggregate دارای یک ریشه (Aggregate Root) و یک مرز (Aggregate Boundary) است. مرز Aggregate مشخص می کند که چیزهایی در آن وجود دارند. Aggregate Root نیز یکی از Entity های داخل Aggregate می باشد و تنها شیء می باشد که اشیاء بیرونی می توانند به آن دسترسی داشته و یا به آن اشاره کنند.

  • قوانین پیاده سازی Aggregate ها
    • Aggregate Root دارای ID منحصر به فرد در سطح نرم افزار بوده اما موجودیت های دیگر در Aggregate، شناسه های محنصر به فرد در سطح همان Aggregate دارند.
    • Aggregate Root مسئول چک کردن Invariant ها می باشد.
    • اشیاء خارج از مرز Aggregate نمی توانند به شیء غیر از Aggregate Root دسترسی داشته و یا به آن اشاره کنند.
    • (با توجه به قانون قبل) تنها Aggregate Root می تواند مستقیما توسط Query از دیتابیس بازیابی شود. به عبارت دیگر به ازای هر Aggregate Root یک Repository تعریف می شود، و اشیاء دیگر مستقیما قابل بازیابی نخواهند بود و باید توسط Aggregate Root مربوطه بازیابی شوند. همچنین دسترسی مستقیم برای ذخیره و یا ویرایش یک موجودیت داخل یک Aggregate بی معنی است و Transaction های دیتابیسی در سطح یک Aggregate معنی دار می باشند.
    • اشیاء داخل Aggregate (اعم از خود Aggregate Root) می توانند به Aggregate Root های دیگر اشاره کنند. این اشاره کردن توسط Identity انجام می شود.

 

  • پیاده سازی مثال Order

در مثال مطرح شده در اول مقاله، ۳ شیء Order، OrderItem و Price داخل یک Aggregate تعریف می شوند. Order به عنوان Aggregate Root تعریف شده و مسئولیت کنترل Invariant مطرح شده را دارد. پیاده سازی فوق صرفا جهت یادگیری مفاهیم بوده و با پیاده سازی یک نرم افزار واقعی و عملیاتی فاصله ی زیادی دارد. بسیاری از جزئیات در این مثال لحاظ نشده است، فرضا واضح است که هر OrderItem باید به یک محصول و یا کالا متصل باشد.

کلاس Price که در قالب یک Value Object تعریف شده است :

کلاس OrderItem نیز در قالب یک Value Object تعریف شده است :

 

کلاس Order نیز به عنوان یک Entity و همچنین Aggregate Root تعریف می شود.

همانطور که ملاحظه میکنید Order مسئولیت چک کردن Invariant ها (بیشتر نشدن قیمت سفارش از ۱۰۰۰) را بر عهده دارد و با استفاده از Encapsulation از صحت آنها اطمینان پیدا میکند.

  • جمع بندی

Entity ها و Value Object های مرتبط با توجه به Invariant هایشان در قالب Aggregate ها دسته بندی می شوند. یک Entity به عنوان Aggregate Root تعریف شده و مسئولیت دسترسی به اشیاء داخل Aggregate و همچنین کنترل Invariant ها را برعهده دارد. اشیاء بیرونی تنها می توانند به Aggregate Root دسترسی داشته باشند، و از طریق آن اشیا دیگر داخل Aggregate را تغییر دهند. این مکانیزم باعث می شود تا صحت Invariant ها همیشه رعایت شده و فرآیند های نرم افزار به درستی پیاده سازی شوند.

درباره هادی احمدی

هادی احمدی
برنامه نویس، تحلیلگر و طراح نرم افزار که به فعالیت بر روی بسترهای نرم افزاری مایکروسافت مشغول هستم. علاقه مند به مباحث طراحی و معماری نرم افزار و همچنین پیاده سازی سیستم های اطلاعاتی پیچیده می باشم.

12 نظر

  1. سلام
    با تشکر از مقاله مفیدتون ،
    گاهی تشخیص مجموعه اشیای مرتبط با همدیگه به عنوان یک اگریگیت با زیاد شدن روابط بین موجودیت ها دشوار میشود ، اجازه دهید سوالمو با یک مثال بپرسم.
    موجودیت “شخص” و “شرکت” را در نظر بگیرید ، هر دو نیاز به ویژگی به نام “آدرس ها” دارند که این ویژگی ، مجموعه ای از آدرس های شخص یا شرکت را نگهداری میکند.(مثلا آدرس محل کار ، منزل یا آدرس شعبه ۱ ، ۲ ، ۳ و…)
    حال کلاس آدرس جزو کدام اگریگیت محسوب میشود؟

    • هادی احمدی

      سلام
      خواهش میکنم
      “آدرس” در مثال فوق یک Value Object است که می تواند در هر دو Aggregate استفاده شود. یعنی هم شخص و هم شرکت می توانند لیستی از شیء Address را در خود نگهداری کنند. هر کدام هم می توانند Invariant های دلخواه خود را در این رابطه بررسی کنند. برای مثال در Aggregate شرکت، دو کلاس شرکت و آدرس موجود هستند که شرکت AggregateRoot تعریف شده است. شرکت میتواند به عنوان AggregateRoot یک سری Invariant های دلخواه را در رابطه با آدرس بررسی کند. در عین حال شخص نیز در Aggregate مربوط به خود می تواند با آدرس رابطه داشته باشد و Invariant های دیگر را بررسی کند.

      • فکر میکنم با این پاسخی که فرمودید که آدرس در هر دوی aggregate ها حضور داشته باشه boundry هر کدام از aggregate نامشخص باشه یا اینکه aggregate ها میتونن اشتراک داشته باشند؟

        • هادی احمدی

          به اشتراک گذاری Value Object بین دو Aggregate به معنی مشخص نبودن Aggregate Boundary نیست. البته نکته ی بسیار مهم اینکه این اشتراک گذاری را زمانی میتوانید انجام دهید که (برای مثال، در سوال مطرح شده) دو آدرس فوق از نظر Domain واقعا یکی باشند. یعنی در Ubiquitous Language مفهوم “آدرس مشتری” و “آدرس شرکت” یک تعریف و در عمل یک رفتار و خصوصیات دارند. به همین دلیل این کار باید با بررسی دقیق Domain و اطمینان از آن انجام شود.

          Aggregate Boundary در واقع مشخص کننده ی مرزهایی است که Consistency مربوط به Invariant ها و Business Rule ها در یک Aggregate باید حفظ شوند.

          • استاد سلام خسته نباشید ممنونم از مقاله خوبتون یه سوال الان تو مثالی که مطرح شده برای آدرس که به صورت value object هم برای شرکت و هم برای شخص استفاده میشه
            و اینکه این آدرس در این دو aggregate مشترک هست
            ما این آدرس رو که مشترک هست بین دو تا aggregate رو تو ساختار پروژه باید تو لایه framework بزاریم درسته یا اینکه باید تو خود لایه domain باشند؟؟
            خیلی ممنونم مرسی

          • هادی احمدی

            سلام، خیلی ممنونم

            از آنجا که Value Object ها اجزای سازنده Domain Model هستند، باید داخل Domain باشند.

  2. سلام
    ممنون از مقاله تون

  3. سلام . ممنون از مطللب خوبتون. در ارتباط مبحث Aggregate سوالی داشتم. در نرم افزاری که توسعه میدیم فرض بر اینه که موجودیتی به نام Customer حاوی لیستی از Sale و موجودیت Sale نیز خود تعدادی SaleDetail و هر SaleDetail درون خود یه موجودیت به نام Product دارد. یک آبجکت Employee هم وجود دارد که در Sale , Customer و Product وجود دارد. اگر مقدوره Aggregate و Aggregate Root در این شرایط رو توضیح بدین. ممنون

    • هادی احمدی

      سلام، خواهش میکنم
      همانطور که در مقاله هم عنوان کردم طراحی دقیق مدل نیازمند داشتن اطلاعات دقیق در مورد Invariant ها و به طور کل فضای مساله است. من با برداشت شخصی خودم از همین چند خط شما، مدلی که توی ذهنم هست رو مشخص میکنم.
      به طور کل چهار Aggregate در مثال مطرح شده وجود دارد :

      .. Sales
      …… Sale = Entity, Aggregate Root
      …… SaleDetails = Value Object

      .. Customers
      …… Customer = Entity , AggregateRoot

      .. Products
      …… Product = Entity, Aggregate Root

      .. Employees
      …… Employee = Entity, Aggregate Root

      • آقا هادی سلام
        در راه حلی که پیشنهاد داده اید عملا سه Agg Root آخر بدون تعریف هیچ Entity یا VO مشخص شده اند. به نظر شما بهتر نیست مثلا در Employee و Customer موجودیت SalesVO داشته باشیم و در Sales نیز ProductVO جایش خالی به نظرم می رسد.

        نظر شما چیست؟ (بیشتر هدفم این است که ببینم concept مساله ر ا فهمیده ام یا خیر)

        • هادی احمدی

          سلام محمد جان
          من فکر میکنم شما بین رابطه ی Association و در یک Aggregate بودن کلاس ها، دچار اشتباه شدید. اینکه کلاس Customer و Sale در دو Aggregate جدا هستند، به این معنا نیست که با هم رابطه ندارند. مسلما این دو کلاس با هم رابطه دارند (برای مثال هر Sale مربوط به یک Customer است). اما چیزی که باعث می شود در دو Aggregate جدا باشند، Invariant های مساله است.

          اینطور به قضیه نگاه کنید : آیا فضای مساله ایجاب میکند برای ایجاد و تغییر و به طور کل کار کردن با Sale، از طریق کلاس Customer اقدام کنیم تا این کلاس Invariant ها را کنترل کند؟ یا صرفا وجود Customer کافی است تا بتوانیم یک Sales ایجاد کنیم و فرآیند های آن را پیاده سازی کنیم؟
          همین سوال ها رو در مورد رابطه ی Sale و SaleDetail (و یا رابطه ی Order و OrderItem در مقاله) بپرسید تا به تفاوت پی ببرید.

دادن پاسخ بهمحمد حسین بی خیال پاسخ!

آدرس ایمیلتان منتشر نمیشودگزینه های الزامی ستاره دار شده اند *

*

شما می‌توانید از این دستورات HTML استفاده کنید: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">

رفتن به بالا