محمد رضا رحیمی
محمد رضا رحیمی
خواندن ۶ دقیقه·۴ سال پیش

درون لایه Domain(دامنه) دقیقا چه خبر است؟

یک برنامه بر مبنای CRUD خیلی چیزی فراتر از ذخیره و بازیابی اطلاعات ندارد. در این جور برنامه ها معمولا مدل های داخل برنامه ما تنها به عنوان یک ظرف برای انتقال اطلاعات عمل میکنند(Data Transfer Object).

اماشرایطی را در نظر بگیرید که یک عملیات در برنامه(مثل سفارش یک محصول) بر روی وضعیت بخش زیادی از سیستم تاثیر می گذارد(مثلا بر روی سیستم انبار و یا حسابداری و غیره). سئوال این است که این طور کد ها را در کجا قرار دهیم طوری که با کدهای CRUD ترکیب نشود؟
لایه دامنه بهترین جا برای قرار دادن Logic برنامه است.

در پایان این نوشتار می خواهیم با ماهیت اصلی لایه دامنه و کارکرد آن بیشتر آشنا شویم.

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

مدل سازه ماکارونی
مدل سازه ماکارونی

این مدل
1. کوچک شده است
2. شبیه سازه اصلی است
3. برخی از مشخصه ها مانند میزان مقاومت در مقابل فشار را شبیه سازی میکند

مدل موجود در لایه Domain هم باید دنیای واقعی را برای ما شبیه سازی کند(مثلا فرایندهایی که در یک فروشگاه اتفاق می افتد). اما این شبیه سازی چه ویژگی هایی دارد؟
1. این شبیه سازی در حافظه اصلی کامپیوتر انجام می شود.
2. ابزار ساخت این مدل Object Oriented است
3. باید تا حد ممکن مشخصات، رفتار ها و اتفاقات دنیای واقعی را شبیه سازی کند.

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

- یک Domain مجموعه ای از مدل ها یی است که میتوانند با هم تعامل داشته باشند.

در واقع هر بار که کاربر یک درخواستی را به نرم افزار ارسال کرد ما یک نمونه از مدل های مورد نیاز Domain را در حافظه اصلی ایجاد می کنیم.

توجه کنید که ما قطعا نمی توانیم یک مدل کامل از کل وضعیت جاری سیستم را در حافظه اصلی داشته باشیم(چون همه وضعیت سیستم در حافظه اصلی جا نمیشود). ما باید سعی کنیم فقط آن قسمت از وضعیت سیستم را که در انجام آن درخواست درگیر است را شبیه سازی کنیم.

کاربر درخواست خود را به نرم افزار ما ارسال میکند. و ما سعی میکنیم مدلهایی که برای پردازش این درخواست نیاز است را شبیه سازی کنیم. می دانیم که همه درخواست های کاربران در پایان منجر به یک سری تغییر در وضعیت سیستم می شود. پس بعد از اینکه درخواست کاربر بر روی ِDomain شبیه سازی شده پردازش شد، مدل ما باید بتواند به ما بگوید که:
آیا این درخواست قابل انجام است؟
1. اگر قابل انجام نیست، به چه علت؟
2. و اگر قابل انجام است، بطور دقیق چه تغییراتی در هنگام شبیه سازی در هر یک از مدلها ایجاد شد؟

در حالت اول ما این موضوع را مستقیما به کاربر اطلاع می دهیم ولی در حالت دوم باید مجموعه عملیات های لازم برای اعمال واقعی تغییرات را (مانند یک یا چند عمل CRUD بر روی Database و یا فراخوانی یک وب سرویس) انجام دهیم.

- اکنون منطق برنامه ما بدون وابستگی به Database یا هر سرویس خارج از RAM قابل Test کردن است

- همچنین کد های مربوط به کار با پایگاه داده و فراخوانی یک سرویس خارجی از کد های منطق برنامه جداست

برای سادگی بیائید یک مثال ملموس تر را بررسی کنیم

فرض کنید یک درخواست ثبت سفارش داریم، این درخواست با موجودیت های زیر سر و کار دارد(به عبارت دیگر این رکوردها در عملیات سفارش درگیر هستند)

  • یک رکورد مشتری
  • چندین رکورد محصول
  • یک رکورد سفارش
  • چندین رکورد ردیف محصول (منظور همان رکورد تعداد هر محصول است)

پس برای شبیه سازی عملیات سفارش این کارها را انجام میدهیم:

1. محصول های مورد تقاضا را از پایگاه داده دریافت می کنیم.

2. رکورد مشتری را از پایگاه داده دریافت می کنیم

3. یک رکورد سفارش جدید (در حافظه) ایجاد می کنیم.

4. محصولات(دریافت شده از پایگاه داده) را به همراه تعداد آن به سفارش اضافه میکنیم

5. مشتری(دریافت شده از پایگاه داده) را در سفارش ثبت می کنیم

6. دستور صدور صورتحساب از سفارش را صادر می کنیم و یک رکورد جدید صورتحساب دریافت میکنیم .

7. رکورد های سفارش و صورتحساب را در پایگاه داده ذخیره میکنیم.

ما قطعا برای شبیه سازی مدل نیاز به همه مشتری ها و همه محصولات نداریم. بنابراین فقط محصولات و مشتری مد نظرمان را از پایگاه داده فراخوانی کردیم

موارد 3 تا 6 تمام منطق ثبت سفارش را در خود دارد. همچنین تنها باعث تغییر بر روی مدل موجود در حافظه اصلی میشود، بدون وابستگی به هیچ چیز دیگر.

افزودن موجودیت های جدید سفارش و صورتحساب تغییراتی است که باید در پایگاه داده ذخیره شود.

موارد 1،2 عملیات های ساده ذخیره و بازیابی اطلاعات هستند و هیچ چیزی از منطق نرم افزار را در خود ندارند.

جداسازی لایه منطق دامنه از منطق ذخیره و بازیابی
جداسازی لایه منطق دامنه از منطق ذخیره و بازیابی

شی گرایی(OOP) در لایه دامنه

همانطور که گفته شد ابزار طراحی این مدل Object Oriented است. قطعا لایه دامنه بهترین جایی است که میتوانید از امکانات OOP استفاده کیند. زیرا مدل های کسب و کار فقط حاوی اطلاعات نیستند. آنها اغلب علاوه بر اطلاعات رفتار ها و رویداد هایی هم در خود دارند. و این فقط از عهده یک زبان Object Oriented برمی آید.

نکته دیگری که در مورد این لایه باید بدانیم این است که مدل کسب و کار نباید اجازه دهد که در یک وضعیت نامعتبر قرارگیرد. برای همین رعایت اصل Encapsulation در این لایه بسیار حیاتی است.

رصد کردن تغییرات(Changes Tracking) در لایه دامنه

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

اما پس از اینکه مدل های دامنه شبیه سازی و درخواست بر روی آن پردازش شد، باید بتوانیم تشخیص دهیم که برای اعمال این تغییرات در وضعیت واقعی سیستم(مثل Database) چه کار هایی باید انجام شود. معمولا این کار به دو صورت انجام میشود.

1. رویداد (Event) ها
2. رصد کردن تغییرات (Changes Tracking)

یکی از عناصر مهم لایه دامنه Event ها هستند، همه تغییراتی که باید به دنیای بیرون از مدل اعمال شود، تغییر در پایگاه داده نیست، بعضی از آنها چیزی فراتر از تغییر در پایگاه داده و CRUD هستند، مانند ارسال یک پیام کوتاه. در این موارد Event ها تنها راه ارتباط مدل با دنیای خارج از مدل است.

اما Changes Tracking امکانی است که از طریق آن میتوانید تغییرات اعمال شده بر روی یک Object را رصد کنید(از طریق پروکسی کردن مدل ها). هرچند ابزار های زیادی برای این کار وجود دارد اما تقریبا تمام ORM ها هم از این امکان پشتیبانی میکنند و معمولا شما نیازی به پیاده سازی آن نخواهید داشت. شما با یک دستور Commit و یا Save Changes تمام تغییرات اعمال شده در مدل ها را در پایگاه داده خود ذخیره می کنید.

مفاهیم اصلی در لایه دامنه

مفهوم Entity با اختلاف مهمترین مفهمومی است که در طراحی لایه دامنه در اختیار داریم، مهمترین امکانی هم که در اختیار ما میگذارد این است که موجودیت ها را فقط توسط یک شناسه از هم تفکیک می کند.

مفهوم ValueObject یک راهکار خوب برای جلوگیری از کدهای تکراری است. ValueObject کمک می کند نوع اطلاعات Entity محدود به داده های اولیه مثل عدد، رشته و بولین نباشد.مثلا آدرس ایمیل میتواند بجای یک رشته یک ValueObject باشد. در غیراین صورت مجبورید هر بار درست بودن ایمیل را بررسی کنید. و این کار ممکن است بار ها تکرار شود. با ValueObject آن را در یک نقطه انجام میدهید. لازم است بدانیم همانطور که داده های اولیه مثل رشته، عدد و غیره توسط محتوایشان از هم تفکیک می شود ValueObject ها هم با محتوایشان از هم تفکیک میشوند.

منظور از Agreggate مجموعه Entity هایی هستند که همواره به همراه هم ذخیره و بازیابی میشوند. یعنی جدای از هم معنی ندارند و کاملا به هم وابسته هستند(مانند سفارش و ردیف محصول در مثال بالا). Entity که محور بقیه Entity هاست AgregateRoot نام دارد(مانند سفارش در مثال بالا).

dddلایه دامنهdomain
شاید از این پست‌ها خوشتان بیاید