مهدی فلامرزی
مهدی فلامرزی
خواندن ۱۱ دقیقه·۲ سال پیش

مسیر تفکر در طراحی Model Driven (قسمت اول)

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

زمانی که پروژهای DDD از معماری CQRS بهره میبرند، بدلیل اینکه به کارگیری و طراحی Domain Model ها بصورت انحصاری، تنها برای Command ها انجام میشود، استفاده از الگوها و قواعد Model Driven به اوج خود میرسد. در چنین شرایطی، غایت طراحی بوجود آوردن مدل هایی است که رفتارهای سیستم مطلوب را به خوبی Encapsulate کرده اند. این پیاده سازی به گونه ایی است که برای اجرای Use Case های مربوط به یک مدل، آن مدل در Scope خود نیازمند ارتباط با بیرون از خود نیست. اصطلاحا به چنین مدل هایی Rich میگویند.

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

زمانی که چنین پدیده هایی رخ میدهند، از نظر تحلیل سیستمی، اغلب نشان از این دارند که یک مسیر یا دیسیپلین مشخصی، برای انجام آن کار، یا شروع به انجام آن کار وجود ندارد. در این مقاله سه قسمتی سعی میشود با کمک مشخص کردن معیارهایی برای طراحی در پارادایم Model Driven، یک مسیر تفکر ایجاد شود. کاربرد این مسیر در یکسو سازی انرژی است که تیم توسعه، آن را برای طراحی بصورت گروهی، صرف میکند. همچنین میتوان از معیارهای مشخص شده به منظور سنجش کیفیت طراحی ها نیز استفاده کرد تا با بازبینی و بازنویسی های صورت گرفته بتوان به مدل های Rich دست پیدا کرد.

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




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



سیستم های OLTP (Online Transaction Processing):

هدف از طراحی این سیستم ها پاسخ دهی، پردازش و ذخیره درخواست های Transactional بصورت Real Time هست. این نوع سیستم ها به سبب تقسیم بندی انجام شده، به گونه ایی طراحی میشوند که حجم بسیار بالایی از اطلاعات Transactional را پشتیبانی کنند.

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


سیستم های OLAP (Online Analytical Processing):

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

این ویژگی در سیستم های OLAP سبب میشود که از پایگاه های داده با امکانات گزارش گیری پیشرفته استفاده شود.به همین دلیل طراحی مدل ها بصورت Data Driven در این سیستم ها بسیار متداول است.


دستیابی به مدل ها از طریق ID:

بعد از معرفی این دو سیستم از دید پردازش اطلاعات، می توان در رابطه با این موضوع بحث کرد، در یک پروژه سازمانی که با رویکرد DDD توسعه می یابد و از معماری CQRS بهره می برد، تیم توسعه در حال ساخت چه سیستم هایی است؟ با تطبیق ویژگی های سیستمی که در بخش های قبل بحث شد، با مرزهای مشخص شده از نظر کارایی فنی در چنین محصولی، به وضوح میتوان به این نتیجه رسید که قسمت Write سیستم منطبق بر OLTP و قسمت Read سیستم منطبق بر OLAP خواهد بود.

بنابراین در اینجا میتوان نتیجه گرفت که تفکر طراحی Model Driven باید بیشتر منطبق بر OLTP باشد. به بیان دیگر در یک طراحی Model Driven خوب، دستیابی به Aggregate ها بیشتر توسط ID آن ها صورت خواهد گرفت تا توصیف و یا ویژگی ایی از آن ها. میتوان اینگونه تصور کرد که برای قسمت Write تنها یک پایگاه داده key:value در اختیار است و برای انجام این مهم از الگو های طراحی شی گرایی در سطح پیشرفته استفاده میشود. این مورد را میتوان به عنوان اولین معیار در طراحی خوب Model Driven در نظر گرفت.


اجتناب از Down Casting برای مشتق شده های یک Aggregate:

معمولا در پروژه های DDD شرایطی پیش میاید که رفتارهای مشترک با پیاده سازی های مختلف شناسایی میشوند. یکی از راه حل ها استفاده از Inheritance هست. زمانی که از این تکنیک بصورت صحیح استفاده میشود هیچ وقت نیاز نیست در فلو های اصلی عمل Down Casting صورت گیرد. این قضیه هنگام فراخوانی Aggregate ها از Repository نیز صادق است. به بیان دیگر یک Aggregate اگر مشتق شده باشد در همه جای Domain بغیر از زمان new شدن باید با phase کلاس base آن دیده شود. این موضوع در زمان فراخوانی یک Aggregate مشتق شده بسیار حائز اهمیت است. این مورد را میتوان به عنوان دومین معیار در طراحی خوب Model Driven در نظر گرفت.


جداسازی Use Case ها در لایه های پایینی:

برای مشخص کردن این موضوع خیلی کوتاه در مورد اینکه کدام لایه ها پایینی یا بالایی هستند بحث میکنیم. عموما پروژه های DDD از معماری های Domain Centric بهره میبرند. بصورت عمومی لایه هایی که اغلب Bootstrapper هستند و یا پیاده سازی Abstract های مختلف را شامل میشوند، لایه های پایینی اطلاق میشوند. در معماری های Domain Centric نظیر Onion بیرونی ترین لایه، قسمت های لایه هایی پایینی محسوب میشوند.



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


ارتباط دادن Aggregate Root ها بجای استفاده از Domain Service:

در پروژه های DDD معمولا طراحی به اینصورت دیده میشود که Aggregate ها باهم در ارتباط نیستند و این عمل اغلب با این توجیه انجام میشود که Granularity باید در حدی باشد که بتوان به راحتی یک Aggregate را در BCیی به BC دیگر انتقال داد. اما این در حالی است که در سال های اخیر بسیار تاکید بر آن شده که طراحی مرزهای Sub Domain ها و به طبع آن BC ها باید بصورتی دقیق انجام شود که در ادامه پروژه دستخوش تغییرات بزرگی نشوند. البته پیش از این تجارب تاکید لزوم تفکر و تحلیل صحیح در قسمت Strategic طراحی DDD گواه این واقعیت بوده است که این قسمت از پروژه های نرم افزاری، عموما یک تصمیم معماری غیر قابل بازگشت است. بنابراین میتوان توجیه Granularity برای طراحی Aggregate ها را عملا ناکارآمد دانست.

همچنین برای بحث در مورد این موضوع، بسیار مفید است که به بیان یک اصل درمورد پیچیدگی نیز پرداخت: "در یک سیستم بسته پیچیدگی هیچگاه از بین نمیرود بلکه ممکن است از جایی به جای دیگر منتقل میشود". این جمله در بعضی از مطالب به قانون پایستگی پیچیدگی و یا قانون Tesler معروف است. در Model Driven سعی شده است با ابداع و یا جمع آوری الگو های طراحی نظیر Aggregate Root ، ارتباطات بین کلاس ها و یا در اصطلاح Coupling بین آن ها را کنترل کرد. چون ارتباطات و Coupling بین اجزا قسمتی از ویژگی های یک پدیده پیچیده است، میتوان این بخش را با استفاده از پیچیدگی تحلیل کرد.

برای استفاده از الگو طراحی Aggregate Root انجام Classification و Fragmentation بر روی بیزینس لاجیک ها غیر قابل اجتناب است. این در حالی است که در همه Use Case ها این امکان وجود ندارد که یک بیزینس لاجیک Classify شده بصورت تنها به کار گرفته شود. عدم توجه به این نکته غالبا خود را بصورت Aggregate هایی با Hierarchy عریض و طویل نمایش میدهد. این در حالی است که قانون پایستگی پیچیدگی سبب میشود زمانی که Use Caseی به یک Fragment از لاجیک دیگر نیاز داشت، این نیاز ارتباطی با استفاده از Domain Service انجام شود. در اینصورت طراحی Aggregate ها هرچه بیشتر به سمت Anemic شدن پیش خواهد رفت. رعایت اصل پایستگی پیچیدگی و استفاده از الگو های طراحی شی گرا، در زمانی که به Fragment های مختلف از بیزینس لاجیک ها نیاز باشد را میتوان به عنوان چهارمین معیار طراحی خوب Model Driven در نظر گرفت.

تا به اینجا معیارهای بیان شد که ارتباط مستقیمی با طراحی Model Driven داشتند. در ادامه به دو معیار فرعی برای طراحی اشاره میکنیم که بصورت غیر مستقیم بر Model Driven تاثیر گذار هستند.


جداسازی Process های Sync و Async:

در پیاده سازی Use Case ها اغلب شرایطی دیده میشود که یک Use Case شامل چندین عدد زیر مجموعه اجرایی در قالب Use Case های دیگر باشد. در پروژه های DDD با بهره گیری از CQRS، این موضوع بصورت Dispatch شدن یک Sequence از Commandها پیاده سازی میشود. عموما مشاهده میشود که حتی Use Case هایی که بصورت Side Effect هستند نیز در پروسه اصلی قرار میگیرند. در حالی که Use Case های Side Effect عموما میتوانند بصورت Async در پروسه دیگر قرار بگیرند. با استفاده از Event ها میتوان پردازش این مدل Use Case ها را در پروسه دیگر انجام داد. این موضوع حتی اگر Handler ها در یک مرز سیستم نیز باشند صادق است. بنابراین میتوان جداسازی Process های Sync و Async را پنجمین معیار طراحی خوب Model Driven در نظر گرفت.


استفاده از Saga Choreography در زمان ارتباطات برون مرزی سیستمی:

ارتباطات بین مرزی سیستمی همیشه یک بحث چالش برانگیزی در مهندسی نرم افزار بوده است. بیان این نکته بسیار حائز اهمیت است که الگوهای معماری ایی مانند Bounded Context اساسا تاکید بر استقلال مرزها دارند. بنابر این ارتباطات و وابستگی های برون مرزی بیش از اندازه، مخل موضوع استقلال مرز ها هستند. در نتیجه در پروژه های DDD انتظار میرود که اکثر Use Case های Sync بصورت درون مرزی پاسخ داده شوند و نیاز چندانی به ارتباطات برون مرزی نداشته باشند. تقابل این موضوع در طراحی ماکروسرویس ها، سو تفاهمی است که بخشی از آن باید در مقاله دیگر بررسی شود و بخش دیگر آن در مقاله تفاوت دیدگاه در طراحی های کلاسیک و DDD بررسی شده است.



در نتیجه در پروژه های DDD باید به دید یک استثنا به ارتباطات برون مرزی درون سیستمی نگریست. از این رو لازم است که برای حفظ Loose Coupling سیستم ها و همچنین Deploy شدن آنها بصورت مستقل، از
Saga Choreography استفاده کرد. این موضوع استفاده از Stateهای میانی برای بعضی از Aggregate ها را نتیجه میدهد. بنابراین استفاده از Choreography و همچنین Stateهای میانی برای بعضی از Aggregate ها را میتوان به عنوان ششمین معیار طراحی Model Driven خوب مطرح کرد.



جمع بندی:

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

  • دستیابی به مدل ها از طریق ID
  • اجتناب از Down Casting برای مشتق شده های یک Aggregate
  • جداسازی Use Case ها در لایه های پایینی
  • ارتباط دادن Aggregate Root ها بجای استفاده از Domain Service
  • جداسازی Process های Sync و Async
  • استفاده از Saga Choreography در زمان ارتباطات برون مرزی سیستمی
model drivendomain driven designdomain modelsoftware engineeringمعماری نرم افزار
مشاور و معمار نرم افزار در شرکت SmartMed
شاید از این پست‌ها خوشتان بیاید