با کمی بررسی در مقالات و آموزشها و مباحث روز دنیای نرم افزار، کاملا مشهود است که یکی از داغترین مباحث مهندسی نرمافزار این روزها Domain Driven Design که از این به بعد به آن DDD میگوییم است. در این مطلب قصد ندارم زیاد به عمق DDD وارد شوم و در این مرحله تنها به ذکر این مطلب بسنده میکنم که DDD برای موفقیت پروژههای نرم افزار الگوها و اصول متفاوتی را ارائه داده است که برخی از این الگوها بیشتر مربوط به شناخت نرم افزار میشوند که اصلاحا آنها را Strategic Pattern مینامیم و دسته دیگر که بیشتر مربوط به نحوه پیاده سازی Domain Model میشود را Tactical Pattern مینامیم.
هنگامی که بحث از توسعه نرمافزار برای یک کسب و کار به میان میآید مهمترین نکتهای که ذهن هر توسعهدهندهای را به خود مشغول میکند، تجزیه، تحلیل و شناخت کسب و کار است. برای شناخت کسب و کار روشهای مختلفی وجود و قطعا یکی از مهمترین روشهای شناخت صحبت و ارتباط برقرار کردن با افرادی است که کسب و کار را به خوبی میشناسند که به آنها Business Expert میگوییم برای سادگی کار از این به بعد این افراد را BE مینامیم. اما نکته اصلی اینجاست که وقتی ما کسب و کاری را نمیشناسیم پس زبان BEها را هم نمیشناسیم و وقتی زبان کسی را بلد نباشیم چگونه میتوانیم با او ارتباط برقرار کنیم؟
پس باید به دنبال راهکاری باشیم که بتوانیم با افرادی که زبان مشترکی نداریم ارتباط برقرار کنیم و مفاهیمی که ایشان منتقل میکنند را به خوبی درک کنیم. با کمی تعمق بیشتر مشکل دیگری را نیز خواهیم یافت، کلمات با توجه به محدوده کاربردی که دارند معانی مختلفی خواهند داشت مثلا هنگامی که در مورد Product صحبت میکنیم اگر در حوزه تبلیغات باشد مفاهیم و خواصی مثل تصویر، اندازه تصویر، شعار تبلیغاتی و ... برای ما معنا دارد و اگر در حوزه خرید فروش صحبت کنیم مفاهیمی مانند تامین کننده و قیمت خرید و ... معنا پیدا میکند. پس باید بتوانیم محدوده اعتبار زبان مشترک خود را نیز تعیین کنیم. این محدوده اعتبار زبان در ادبیات DDD اصطلاحات Bounded Context نامیده میشود. البته Bounded Context کاربردهای دیگری نیز دارد که فعلا به آنها اشارهای نمیکنیم.
با این شرایط کار کمی ساده تر میشود، ما محدودهای تعیین میکنیم و زبان مشترک و شناخت خود را از دامنه را توسعه میدهیم و کم کم به یک زبان مشترک و قابل درک برای همه اعضای تیم از BEها تا توسعهدهندهها و Testerها و ... میرسیم. برای ساده شدن کار و جلوگیری از اشتباه و کج فهمی باید همه جا از این زبان استفاده کنیم. همه جا یعنی در جلسات، در مدلهای منطقی، مدلهای کدنویسی، مستندات و ...
به این زبان مشترک که همه جای پروژه از تجزیه و تحلیل گرفته تا کدنویسی و نمودارهای UML و ... استفاده میشود هم اصطلاحا Ubiquitous Language میگوییم و از این به بعد برای سادگی کار به این زبان هم UL میگوییم.
تا اینجای کار به نظر همه چیز خوب و ساده میآید. اما نکته اصلی اینجاست که چطور میتوانیم BEها را وارد بازی کنیم؟ چطور مفاهیمی که بیان میکنند را متوجه شویم؟ چگونه آنها را وادار کنیم که روی مسئله ای که برای ما اهمیت دارد متمرکز باشند؟ اگر جواب این سوالها را بدهیم به مرحله بعدی میرویم و آن این است که چطور بفهمیم کدام کلمات و مفاهیمی که بیان میکنند اهمیت دارد؟ با فرض اینکه تا اینجای کار را هم به خوبی جلو رفته باشیم باید به این فکر کنیم که چطور میتوانیم برای حجم زیاد کلماتی که به دامنه زبانی ما اضافه شده است محدوده تعیین کنیم؟
البته این تعیین محدوده در شرایط دیگری نیز برای ما حیاتی است. اگر از علاقه مندان به میکروسرویسها باشید هم حتما میدانید که تعیین محدوده برای سرویسها همیشه یکی از چالشهای استفاده از این معماری معروف و محبوب این روزها است.
در این سری مطالب قصد داریم با هم یکی از تکنیکهای جوان و محبوب این حوزه با نام EventStorming را بررسی کنیم.
2. استفاده از لیست نیازمندیها برای شناخت مسئله:
یکی از اشتباهات رایج برنامه نویسها استفاده از لیست نیازمندیهای برنامه برای شناخت نرم افزار است. در جلساتی که با BEها تشکیل میدهیم از آنها میخواهیم لیستی از نیازمندیها در اختیار ما قرار دهند و در نهایت ممکن است با لیستی مانند زیر مواجه شویم:
هر چند لیست بالا بسیار ساده و ناقص نوشته شده است ولی با کمی دقت میتوانیم ایراد بزرگی در لیست بالا بیابیم. اما برای اینکه ایراد روش بالا را دقیقتر متوجه شویم نیاز داریم که تعریفی از فضای مسئله و فضای راهحل داشته باشیم.
2.1. فضای مسئله و فضای راه حل:
هنگامیکه میخواهیم برنامه را توسعه دهیم در وضعیت جاری هستیم و میخواهیم به وضعیت مطلوب برسیم و برای رسیدن از وضعیت جاری به وضعیت مطلوب تعدادی قواعد و قوانین نیز داریم، به این وضعیت ابتدایی و مطلوب و قواعد فضای مسئله میگوییم.
با کنکاش و بررسی دقیق فضای مسئله به چندین راه مختلف برای رسیدن به وضعیت مطلوب میرسیم که همه این راهها ما را به هدف میرساند. بعضی راهها سادهتر است و بعضی دیگر پیچیدهتر. یک راه ممکن است دقت کمی داشته باشد و ساده باشد و روشی دیگر بسیار پیچیده اما دقیق باشد. مجموعه این راه حلها را فضای راه حل مینامیم و وظیفه ما است که از این فضا یکی از راهها را انتخاب و پیاده سازی کنیم. البته ممکن است بعد از مدتی توسعه و پیشرفت در کار متوجه شویم یکی دیگر از راه حلها مناسب تر بوده و کارهای خود را تغییر دهیم.
2.2. ایراد لیست نیازمندیها:
حال که با مفهموم فضای مسئله و راه حل آشنا شدیم احتمالا بسیار سادهتر ایراد لیست نیازمندیها را درک خواهیم کرد. این روش به جای اینکه ما را با فضای مسئله آشنا کند تا بتوانیم فضای راه حل را بیابیم، یکی از راه حلها را انتخاب کرده و آن راه حل را در اختیار ما قرار میدهد و ما به عنوان برنامه نویس به جای خلق یک برنامه کاربردی، کورکورانه از راهحل ارائه شده توسط BE پیروی میکنیم. شاید یکی از دلایل شکست بسیاری از پروژهها همین پیروی بی دلیل از BE است.
البته این روش نکات مثبتی نیز دارد و در جایگاه خاص خود بسیار هم میتواند مفید به فایده باشد. اما اگر از این ابزار به عنوان یک راه حل همیشگی استفاده کنیم ممکن است مانند شمشیر دو لبه به ما آسیب برساند و کمکی برای پیروزی ما نکند.
3. جلسات منفعل:
یکی دیگر از روشهای شناخت احتمالا تشکیل جلسات و بحث و گفتگوی معمول با BEها است. حتما برای شما هم پیشآمده در جلسات کاری خسته کننده و بدون سرو ته و هدف شرکت کرده باشید. جلساتی که توسعه دهندهها BEها را به چشم انسانهای اولیه میبینند که حتی نیازها اولیه خود را نیز نمیتوانند بیان کننده و در مقابل BEها هم تعدادی برنامهنویس از خود راضی و غرغرو و درونگرا را میبینند که به جز چند Keyword برنامهنویسی کلامی دیگر بلد نیستند. در این جلسات که معمولا دور یک میز تشکیل میشود، ابتدا احتمالا BE شروع به صحبت کند و مطالبی کاملا گسسته از صورت مسئله و ترکیب شده با پاسخ را ارائه میکند، در این مرحله قاعدتا هیچ کس درک کاملی از مسئله پیدا نمیکند و با یک کار نصفه و ایراد دار وارد مرحله بعد میشویم. در مرحله بعد برنامهنویسها احتمالا سعی میکنند درک ناقص خود را در قالب چند شکل و نمودار گنگ و غیرقابل درک برای BEها ارائه کنند. در پایان از آنجا که هیچ کدام از طرفین حاضر به قبول ایرادات خود نیستند احتمالا این درک و طراحی ناقص و پر ایراد به تایید طرفین میرسد و با لبخندی حاکی از رضایت جلسه به پایان میرسد و برنامه نویسها با کوهی از علامتسوال و کجفهمی کار توسعه را شروع میکنند و BEها هم با انبوهی از نگرانی، منتظر جلسه بعدی و مشاهده نرمافزار طراحی شده میمانند.
هرچند هدف این جلسات شناخت فضای مسئله است نه ارائه راه حل اما ایرادات زیادی در این جلسات و روالها وجود دارد که باید برای آنها راهکاری بیاندیشیم.
3.1. مشکل فرضیات:
اغلب ما با دانش و پیشزمینه قبلی از یک مسئله وارد جلسات میشویم و اگر با مفهمومی غریب مواجه شویم ابتدا سعی میکنیم در دانش قبلی خود به دنبال معادلی برای این مهفوم باشیم و اگر بتوانیم معادلی با آن مفهموم بیابیم، بدون اینکه از صحت دانش قبلی خود اطمینان داشته باشیم فرض میکنیم مفهموم را میدانیم و سوالی در مورد این مفهوم غریب نمیپرسیم. در این موارد به جای اینکه نیاز BE را بشناسیم و راه حل آن را ارائه کنیم، فرضیه خود را ارائه میکنیم.
3.2. مدل سازی نامناسب:
یکی از کارهایی که در چنین جلساتی بسیار استفاده میشوم استفاده از مدلسازیهای رسمی مثل UML برای ارتباط دوطرفه برقرا کردن است. همه ما به عنوان مهندسین نرم افزار از مزایای این زبان مدلسازی و کاربرد آن بسیار شنیدهایم و اینکه گفته می شود همه این مدل را متوجه میشوند. اما اگر با خودمان صادق باشیم تعدادی باکس و فلش حتی برای خود ما مهندسین نرمافزار که چندین واحد درسی برای این زبان گذراندهایم و در پروژههای مختلفی از این مدل استفاده کردهایم هم مهفوم نیست چه برسد به BEها.
3.3. مشکل متکلم وحده:
در چنین جلساتی معمولا یک نفر شروع به صحبت میکند و بقیه کاملا ساکت منتظر پایان صحبتها میمانند. با این روش احتمال فراموشی نکاتی که حین صحبت نفر اول به ذهن سایرین میرسد بسیار بالا است. بعضا هم اگر یک نفر رشته کلام را بریده و سوالی بپرسد بحث ابتدایی ابتر میماند. همهمه و صحبت افراد مختلف با هم، یکی دیگر از مشکلات متکلمین در این جلسات است.
3.4. عدم وجود راهکار برای ارائه مفاهیم پیچیده:
اگر بخواهیم تعدادی موجودیت را لیست و مدل کنیم مشکل زیادی نخواهیم داشت. اما در این روشها راهکاری برای مدلسازی مفاهیم غیر فیزیکی و ... وجود ندارد.
4. پایان مشکلات Event Storming:
در سال 2013، آقای Aberto Brandolini با توجه به مشکلاتی که بیان شد، روش خلاقانهای را برای برگزاری جلسات شناخت ارائه کرد. در این جلسات مبنا چابکی و ارتباط همه اعضای تیم با هم است و در نهایت کمک میکند در ورکشاپهایی جذاب مسئله شناخته شده و کار به درستی انجام شود. این ورکشاپها با نام Event Storming شناخته میشوند. در ادامه به معرفی کلیاتی از Event Storming میپردازیم.
4.1. روش مدلسازی:
اصل بنیادی در Event Storming ارائه یک روش ساده، قابل درک و فعال برای مدلسازی عملکردهای سیستم است. استفاده از این رویکرد باعث بهبود شناخت، افزایش تعامل و درگیر شدن موثر افرادی میشود که تاثیر زیادی در شناخت صحیح مسئله دارند اما در حالت عادی توانایی تعامل کمتری دارند.
در Event Storming ابتدا رفتار سیستم، مورد توجه قرار میگیرد و تلاش میشود با مرکزیت قراردادن رفتار سیستم، کارکرد کلی سیستم شناخته شود. به طور کلی میتوانیم فرض کنیم هر سیستمی در هر برشی از زمان در یک وضعیت خاص قرار دارد. در ادامه هنگامی که یکی از Actorهای سیستم عملی را در سیستم انجام میدهد، این وضعیت تغییر مییابد. با کمی دقت در میابیم عمل Actor موجب ایجاد شرایط جدید شده است در نتیجه میتوانیم اول بررسی کنیم چه اتفاقی افتاده و سپس ببینیم در ازای این رخداد چه عملی باید انجام دهیم و در چه وضعیتی قرار میگیریم. بیایید با هم یک روال پرداخت قبض را از طریق اینترنت بانک بررسی کنیم.
ابتدا یک سازمان به کمک شناسه قبض انتخاب می شود، سپس با کمک شناسه پرداخت مبلغ قبض تعیین میشود و تایید میکنیم. در نهایت با یک تایید نهایی قبض پرداخت میشود.
از منظر مشتری بانک، مبلغی از حساب وی کسر میشود. پرداختی کامل میشود و قبض پرداخت میشود و دیگر بلا استفاده است. از منظر سازمان دریافت کننده قبض اما شرایط متفاوت است. در بازههای زمانی سازمانها پولشان را دریافت میکنند و همراه آن لیستی از پرداخت کنندهها را هم دریافت میکنند و باید بررسی کنند کدام قبض برای چه فردی پرداخت شده محسوب میشود.
هر عملی که توسط کاربر انجام میشود سیستم در وضعیتی جدید و قابل تشخیص قرار می گیرد.
همانطور که مشاهده میکنید این وقایع، حقیقتهای غیرقابل انکار و بازگشت هستند و اگر بخواهیم وضعیتی را به حالت قبل برگردانیم امکان پذیر نیست. اگر سازمان دریافت کننده قبض متوجه شود که قبض برای مثال دوبار پرداخت شده است، امکان بازگرداندن سیستم به وضعیت قبل وجود ندارد و برای جبران این حالت سیستم باید عملیات جدیدی انجام دهد. این اتفاقات و حقایق را domain event مینامیم. پایهای ترین و مهمترین بخش در Event Storming همین domain eventها هستند. اصلا اهمیت domain eventها را از نام این روش نیز میتوانیم درک کنیم. نکته قابل توجه در domain eventها این است که وقایعی هستند که در سیستم رخ داده است. نه کارهایی که دوست داریم در سیستم انجام شود. این وقایع در گذشته رخ دادهاند، قرار نیست در آینده این وقایع رخ دهند. فرمی وجود ندارد. دکمه و دستوری وجود ندارد. فقط و فقط وقایعی رخ داده است. هر domain event یک حقیقت واقعی و موجود است که تغییری در وضعیت سیستم ایجاد کرده است و ما میخواهیم آن را مدل کنیم.
تا اینجای کار با یکی از مفاهیم جلسات Event Storming که همان domain eventها هستند آشنا شدیم. هر مفهوم در Event Storming به کمک یک sticky با یک رنگ خاص مدل میشود. دقت کنید که رنگها در این کار بسیار اهمیت دارد. به مرور زمان مفاهیم زیادی با تعداد زیادی تکرار به مدل ما اضافه میشود و باید بتوانیم خیلی سریع و راحت مفاهیم مختلف را تشخیص دهیم.
پیشنهاد آقای Brandolini برای انتخاب رنگ برای domain eventها نارنجی است. که در تصویر پایین مشاهده میکنید.
ابتدا یک صورت حساب ایجاد میشود. سپس صورت حساب ارسال میشود. در ادامه ممکن است صورت حساب پرداخت شود یا به مشکل بخورد. همانگونه که مشاهده میکنید این تصویر نکات زیادی را با خود به همراه دارد. به سادگی قابل فهم است و اگر کمی با خود صادق باشید معلوم است که متوجه میشوید هدف سیستم چیست. ترتیب و توالی انجام کارهای سیستم نیز کاملا قابل مشاهده است در بسیاری از موارد باید event وجود داشته باشد تا پیرو آن eventبعدی ایجاد شود. مثلا اگر صورت حسابی ساخته نشده باشد چگونه امکان ارسال آن وجود دارد؟ هیچ قانون خاصی در مورد domain eventها و مدل سازی آنها وجود ندارد. فقط هر event باید دو قسمت داشته باشد، موضوع مثل صورتحساب و عملی که در مورد آن موضوع انجام شده مثل ایجاد شدن، ارسال شدن و ... و با توجه به اینکه رخداد در سیستم رخ داده است باید در زمان گذشته نوشته شود تا مفهوم انجام شدن در گذشته را کاملا بیان کند.
نکتهای که تا اینجا شاید بیان نشده باشد این است که در این مرحله ما کاری به سیستمهای مختلف نداریم. در ابتدا صرفا کل فرایند را باید مدل کنیم و به ترتیب رخدادهای کل فرایند را ثبت کنیم. برای مثال در مورد قبض خدمات، تولید قبض در یک سیستم انجام میشود، پرداخت شدن آن در سیستم اینترنت بانک انجام میشود و بررسی تایید نهایی آن در سیستمی دیگر انجام میشود. حتی ممکن است یکی از مراحل کار به صورت دستی انجام شود و فقط مسئله این است که از رخدادهای سیستم با خبر شویم.
4.2. از خیال واهی به دید کلی و قابل تصور:
همانطور که مشاهده میکنید همین مدل اولیه ارزشهای زیادی را برای افراد حاضر در جلسه ایجاد کرده است. در مدل فعلی هم میتوانیم کل وقایع سیستم را تشخیص دهیم، هم میتوانیم ترتیب و توالی رخدادها را تشخیص دهیم و در نهایت با توجه به شرایط سیستم میتوانیم تا حدودی سیستمهای مختلفی که در انجام فرایند درگیر هستند را تشخیص دهیم.
توانایی درک و تصور کردن سیستم نهایی یکی از مهمترین ویژگیهای انواع روشهای مدلسازی است که Event storming هم از این قاعده مستثنی نیست. به محض ثبت یک event در سیستم، به سادگی میتوانیم آن رخداد و جایگاه آن را درک کنیم و این وضعیت بسیار بهتر از صرفا شنیدن کلمات و لبخند زدن است.
حال که تصویر کلی از سیستم ایجاد شد، تازه سوالات شروع میشود:
با پاسخ دادن به هرکدام از این جوابها eventهای جدیدی شناسایی میشود و در پایان به سادگی میبینیم که مدل ابتدایی و ساده ما دیگر خیلی ساده نیست. اما حسن این مدل پیچیده این است که دیگر کسی با فرضیات خود کار نمیکند و هر کسی دقیقا میداند چه اتفاقاتی باید در سیستم رخ بدهد.
دانستن این نکته خالی از لطف نیست که جلسات event storming مانند جلسات سنتی معمول که همه دور یک میز مینشینند نیست و به صورت stand up برگزار میشود افراد به صورت فعالانهای در ساخت مدل سیستم مشارکت میکنند. پس باید برای برگزاری این جلسات فضای مناسبی را در نظر بگیرید که گنجایش شرکت کنندگان را داشته باشد و در کنار آن فضای مناسب برای حرکت و همکاری را نیز در اختیار قرار دهد. البته باز اگر بیشتر دقت کنیم ما به فضای نامحدودی در افق نیاز داریم تا تمامی Eventها را با ترتیب درست در آن ثبت کنیم. با کمی بررسی در سناریوهایی که به طور معمول با آنها سرو کار دارید متوجه خواهید شد که تختههای وایت برد محدودی که در اختیار داریم برای انجام این کار مناسب نیستند و نیاز به ابزاری بهتر داریم.
نکته ای که در این شرایط آقای Brandolini بیان میکنند این است،" شما چقدر فضا دارید؟ مشکل من بزگتر است".
در صورتی که فضای کاری ما محدود باشد، احتمالا برای حفظ فضا برای موارد مهمتر برخی eventهای کم اهمیت را از سیستم حذف میکنیم و در نظر نمیگیریم و اینجا خطر اشتباه و فهم ناقص ایجاد میشود. پس باید به طریقی مشکل فضای محدود را حل کنیم. یکی از راهکارهای معمول برای حل کردن مشکل محدودیت فضا استفاده از رولهای کاغذی به جای تخته وایت برد یا دیوار است.
5. جمع بندی:
در این نوشته سعی کردیم کمی با ادبیات DDD آشنا شویم و روشهای سنتی شناخت دامنه را بررسی کردیم و با ایرادات آنها آشنا شدیم. سپس به دلیل نیاز به روشی بدیع و نو پرداختیم و با Event storming به عنوان راه حل برخی از مشکلات آشنا شدیم. البته تا اینجای کار شناخت ما از Event Storming کاملا مقدماتی است. در مطالب بعدی در مورد سایر ماهیتهای موجود در Event Storming و اهمیت آنها و نحوه مدلسازی و ارتباطات آنها صحبت خواهیم کرد و به کمک مثالهای عملی بیشتر با این روش تحلیل و شناخت آشنا خواهیم شد.
پ.ن: شب تاسوعاست. معمولا عادت به توی خونه موندن ندارم ولی به دلایل شخصی مجبور به خونه موندن شدم. هر کسی تو این روزها و شبها دست به دعا میشه برای منم دعا کنه!