آموزش DDD- مثال- زبان دامنه برای تبلیغات طبقه بندی شده

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

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

توسعه دهندگان به سرعت تصمیم گرفتند یک خصیصه Status به کلاس دامین ClassifiedAd اضافه کنند. نوع این خصیصه Enum است که نشان دهنده وضعیت های مختلف فرایند بازبینی و انتشار تبلیغ است. همچنین این خصیصه بعدا می توان بعنوان وضعیت هایی که هنوز برای ما ناشناخته هستند و ممکن است بعدا بوجود بیایند نیز استفاده شود. از آنجائیکه آنها می خواهند رفتارها را در مدل دامین پیاده سازی کنند، متد UpdateStatus را نیز به کلاس اضافه کردند که شبیه به کد زیر شد:

https://gist.github.com/msadeqshajari/7b4e0f3fe66c9b6ab619b3a239062af0

حالا که متد، یک رخداد دامین را نیز منتشر می کند، سایر اجزای سیستم می توانند این رخداد را subscribe کنند و عملیات مقتضی با آن را انجام دهند.

ما در ادامه بخش ها در این دوره بیشتر در مورد event ها و command های دامین صحبت می کنیم و توضیح می دهیم.

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

ad.UpdateStatus(ClassifiedAdStatus.Published);

و بعد از اینکه بازبینی به اتمام رسید، تبلیغ فعال می شود:

ad.UpdateStatus(ClassifiedAdStatus.Activated);

به نظر همه چیز قابل قبول است. کلاس ClassifiedAd ما یک state machine است. یعنی جائیکه نمونه هایی از این کلاس در چرخه عمر یک تبلیغ از یک وضعیت به وضعیت دیگر حرکت می کنند. با این وجود، ما هدف را گم کردیم. زبان ما عجیب شد! بجای اینکه بگوئیم ما می خواهیم که تبلیغ را منتشر کنیم، می گوئیم، ما وضعیت تبلیغ را تغییر می دهیم. بجای فعال سازی تبلیغ، مجددا می گوئیم وضعیت تبلیغ را تغییر می دهیم!

بعد از چندین تغییر در سیستم، به نظر می رسد که کد درست کار می کند:

https://gist.github.com/msadeqshajari/6699a80339497ac8e996e9c086cff269

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

https://gist.github.com/msadeqshajari/b9d106b0dac3ea4c8611a68ba32b6719

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

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

تیم تصمیم گرفت که کد را بازنویسی کرده و از زبان مدل متناسب استفاده کند. خروجی کار آنها این کد بود:

https://gist.github.com/msadeqshajari/f0485b7a5e49bae2ea576443f334557c

اکنون، همچنین می توانیم کنترلگر رخداد دامین را نیز به شکل زیر بازنویسی کنیم:

https://gist.github.com/msadeqshajari/6d336646dbed8aa2396fc2313cea742b

سپس برای مدیریت تبلیغاتی که محتوای نامناسب داشتند، می توانیم یک کنترلگر رخداد جدید ایجاد کنیم:

https://gist.github.com/msadeqshajari/7d017c66a59a0109780374236a33c9d7

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

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

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

با تبدیل مفاهیم ضمنی به مفاهیم صریح، نه تنها مفاهیم فراموش شده را در کدهایمان اکتشاف می کنیم، بلکه آن ها را به مدل دامین خودمان نیز می افزائیم. این بخش از کار ضروری است چرا که این زبان در تمام مدل هایی که مربوط به این دامنه هستند، استفاده می شود. هم مدل های کسب و کار و هم مدل های ذهنی، بصری و مفهومی مربوط به دامین در نمودار ها و در کدها. این الگوی استفاده از مفاهیم یکسان، یا به طور کلی، زبان یکسان در مراحل مختلف مدل ها در یک سیستم را زبان مشترک (Ubiquitous Language) می نامند.