کی از موضوعات مهم در Domain Driven Design تصمیم گیری در رابطه با این هست که یک کد در کجای برنامه قرار می گیرد و یا در کدام یک از Business Object ها قرار می گیرد. بر اساس بسیاری از قوانینی که برنامه نویس ها و توسعه دهنده های با تجربه در نظر میگیرند میتوانیم چنین تصمیم گیری های حساسی را مدیریت کنیم.
یکی از چالش هایی که اغلب توسعه دهنده ها همیشه با آن رو به رو هستند تصمیم گیری در رابطه با روش توزیع کردن کد مورد نیاز برنامه در معماری نرم افزارشان است. سوالاتی از قبیل اینکه کدها در کدام یک از نرم افزارهای خاص باید قرار بگیرند یا اینکه کدام کد در tier میانی قرار میگیرد یا اینکه کدام یک در Business Object ها و کدام یک در لایه ی Data Access قرار میگیرند سوالاتی است که اغلب توسعه دهنده ها با آنها روبرو هستند.
همانطور که در ابتدای این آموزش گفتیم توجه به تصمیم ها و نصیحت هایی که اغلب توسعه دهنده های با تجربه از آنها استفاده می کنند می توانند ما را در این نوع تصمیم گیری ها کمک کند.
بر اساس اصول Domain Driven Design کدهای مربوط به هر نیازمندی که مربوط به یک اپلیکیشن خاص میشوند باید در همان اپلیکیشن قرار بگیرند. اگر شما در حال پیاده سازی نیاز مندیها در Use Case ها هستید این به این معنی است که کدی که برای یک نیازمندی پیاده سازی می کنید و در درون یک Use Case تک قرار میدهید باید درون همان اپلیکیشن مرتبط قرار داده شود.'در یکی از مقاله های دیگری که بر روی وبسایت پروید منتشر شده بود در رابطه با اهمیت مجموعه ای از اشیا در Domain Driven Design نسبت به اشیا تک صحبت کردیم. مجموعه ای از اشیا در Domain Driven Design را Aggregate می نامیم. در همان مقاله از Aggregate های مختلفی صحبت کردیم. هر نیازمندی که در زمان استفاده از یک Aggregate استفاده می شود باید درون همان Aggregate پیاده سازی شود.
در درون یک Aggregate به طور کلی سه نوع از Object ها وجود دارند. Object ی که اپلیکیشن به طور مستقیم رفرنس میدهد که به آن Aggregate Root میگوییم. اشیایی که قابل به روز رسانی هستند که به آنها Entity Object میگوییم و اشیایی که Read-Only هستند و در Domain Driven Design به آنها Value Object میگوییم.
علیرغم این موضوع این توصیف در رابطه با حجم کدی که در درون یک Aggregate به آن نیاز دارید غلو میکند. همانطور که در یکی دیگر از آموزش های وب سایت پرووید در رابطه با Value Object ها صحبت کردیم. در درون یک Aggregate بسیاری از اشیا و Value Object خواهند بود که عموما کالکشنی از پروپرتی های Read-Only هستند و Business Logic خیلی کمی در آنها پیاده سازی میشود.
البته در کنار این حجم از کد ها کد هایی هم وجود دارند که در بیش از یک اپلیکیشن مورد استفاده قرار میگیرند و به طور انحصاری مربوط به یک Object خاص نیستند.
در Domain Driven Design چنین کدهایی را در درون یک اپلیکیشن یا Aggregate قرار نمیدهیمِ این کدها را در درون کلاس های جدا شده ای به اسم Domain Service قرار میدهیم. نکته ای که باید بدانید این است که متد های درون کلاس های Domain Service به شکل بسیار تصادفی سازماندهی میشوند. به عبارت دیگر هیچ اصلی برای سازماندهی کد های درون Domain Service و اینکه چه متدی در آنجا قرار بگیرد یا چه متدی در درون Domain Service قرار نگیرد وجود ندارد. بدون شک کلاس ها و متدهایی که اغلب در کنار هم استفاده می شوند یا متد هایی که از روال های یکسانی استفاده می کنند باید در درون یک پروژه یکسان قرار بگیرند. به طور کلی هر اصلی که برنامه نویس برای سازماندهی Domain Service ها استفاده می کند میتواند به عنوان یک مبنای مناسب برای تقسیم کردن کد در چندین Domain Service استفاده شود.
یک مورد خاص از پروژه هایی که شبیه به Domain Service هستند Repository است. کلاس و کلاس هایی که مسئول به روز رسانی و بازیابی اشیا از بانک های اطلاعاتی هستند. دقت کنید که Aggregate ها Storage Agnostic هستند. به عبارت دیگر در رابطه با اینکه چگونه ساخته می شوند و ذخیره می شوند هیچ اطلاعاتی ندارند. برای ساده تر کردن توسعه Repository ها دو توصیه ی طراحی در Domain Driven Design مطرح میشود.
توصیه اول این است که هر Aggregate را با یک Repository تک مرتبط کنیم. به عبارت دیگر یک Repository تک مسئول تبدیل کردن تغییرات یک Aggregate به دستورات آپدیت برای اجرا شدن بر روی بانک اطلاعاتی باشد. اینکه یک رابطه و تناظر یک به یک بین Aggregate ها و Repository ها وجود داشته باشد امری اجباری نیست اما انجام دادن این کار باعث می شود که کد درون Repository ها ساده تر شود و ایجاد تغییرات بر روی آنها آسانتر گردد چرا که تغییرات انجام شده در Repository ها فقط بر روی یک Aggregate تاثیر میگذارد. ضمناً از آنجایی که حجم زیادی از کدهای درون Repository ها توسط Entity Framework تولید می شود نیازی به نگرانی در رابطه با کد اشتراکی بین Repository ها که برای Aggregate های مختلف نوشته میشود نیست.
توصیه دیگر استفاده کردن از CQRS است. یک الگو برای جدا کردن کدهای مربوط به بازیابی داده ها از کدهایی که بر روی داده ها آپدیت انجام میدهند. دقت کنید که در رابطه با این الگو در یکی از کارگاههای آموزشی وبسایت پرووید صحبت کردیم. الگوی CQRS میداند که عملیات بازیابی داده ها از بانک اطلاعاتی اغلب کار ساده ای نیست و ممکن است نیاز به انجام یکسری سازماندهی بر روی داده ها وجود داشته باشد. از این جهت تولید کردن روش هایی که تمامی سناریوهای بازیابی دادهها را پشتیبانی میکنند منجر به پیچیدگی آنها میشوند.
در مقایسه با کد مربوط به بازیابی داده ها کد های مربوط به به روز رسانی ساده تر هستند و اغلب حول محور چندین Entity Object در درون یک Aggregate خاص پیادهسازی میشوند و به طور کلی سه وظیفه را انجام میدهند: به روز رسانی و درج و حذف. از همین جهت است که ما به این نتیجه میرسیم که Model ی که برای بازیابی داده ها استفاده می شود ممکن است از Model ی که برای به روز رسانی داده ها استفاده شود متفاوت باشد. در نتیجه داشتن روشی یکتا برای بازیابی داده ها و روشی یکتا برای ایجاد یا به روزرسانی داده ها می تواند مفید باشد. حتی میتوان به این فکر کرد که داده ها را در زمان بازیابی از یک بانک اطلاعاتی غیر نرمال بخوانیم چرا که سرعت آن میتواند بیشتر شود و به روز رسانی را بر روی یک بانک اطلاعاتی نرمال انجام دهیم.
یکی دیگر از مزایای استفاده کردن از الگوی CQRS این است که شما می توانید دستورات به روزرسانی خود را در زمان صادر شدن آنها انجام ندهید و در زمانی دیگر آنها را انجام دهید و در واقع آنها را به زمانی دیگر موکول کنید. این یک تکنیک در Domain Driven Design است که باعث ساده تر شدن بیشتر کد میشود اما طبیعتاً در این مقاله به آن نمی پردازیم.
منبع: وبسایت پرووید