فصل ۱۰ — راهنماهای اکتشافی طراحی
«بستگی دارد» تقریباً پاسخ درست به هر پرسشی در مهندسی نرمافزار است، اما پاسخ چندان کاربردیای نیست. در این فصل بررسی میکنیم این «بستگی» دقیقاً به چه چیزهایی وابسته است. در بخش اول کتاب ابزارهای طراحی دامنهمحور برای تحلیل دامنههای کسبوکار و اتخاذ تصمیمهای طراحی راهبردی را آموختید. در بخش دوم، الگوهای طراحی تاکتیکی را بررسی کردیم: روشهای مختلف پیادهسازی منطق کسبوکار، سازماندهی معماری سیستم و برقرار کردن ارتباط میان اجزای سیستم. این فصل میان آن دو بخش پل میزند و نشان میدهد چگونه میتوان از ابزارهای تحلیل برای هدایت تصمیمهای مختلف طراحی نرمافزار استفاده کرد؛ یعنی طراحی نرمافزار بهواقع «دامنهمحور» شود.
راهنمای اکتشافی (Heuristic)
راهنمای اکتشافی یک قانون سخت، تضمینشده یا اثباتشدهٔ ریاضی نیست که در صددرصد موارد درست باشد. بیشتر یک قاعدهٔ سرانگشتی است: ممکن است کامل نباشد، اما برای هدف فوری ما به اندازهٔ کافی خوب است. رویکرد اکتشافی در حل مسئله، بهجای غرق شدن در تمام نشانههای پرسروصدا، روی نیروها و نشانههای غالب و تعیینکننده تمرکز میکند. راهنماهای این فصل نیز روی ویژگیهای اساسی انواع دامنههای کسبوکار و ماهیت مسئلههایی متمرکزند که هر تصمیم طراحی قرار است حل کند.
زمینههای محدود (Bounded Contexts)
همانطور که در فصل ۳ دیدیم، هم مرزهای عریض و هم مرزهای باریک میتوانند تعریف یک زمینهٔ محدود معتبر را برآورده کنند؛ به شرط آنکه یک زبان فراگیر سازگار را درون خود نگه دارند. پس اندازهٔ بهینهٔ یک زمینهٔ محدود چیست؟ این پرسش بهویژه از آن جهت مهم است که زمینهٔ محدود اغلب با میکروسرویس یکی انگاشته میشود. آیا همیشه باید کوچکترین زمینهٔ محدود ممکن را هدف بگیریم؟
برای تعریف مرزهای یک سرویس، راهنماهای اکتشافی مفید و روشنگر زیادی وجود دارد. اندازه یکی از کمفایدهترین آنهاست.
— Nick Tune
بهجای اینکه مدل را تابع اندازهٔ مطلوب کنیم و همهچیز را برای زمینههای کوچک بهینه کنیم، بهتر است جهت رابطه را معکوس کنیم: اندازهٔ زمینهٔ محدود باید تابع مدلی باشد که در بر میگیرد.
تغییری که همزمان چند زمینهٔ محدود را تحت تأثیر قرار دهد، پرهزینه است و هماهنگی زیادی میطلبد، مخصوصاً اگر تیمهای متفاوتی مالک آن زمینهها باشند. تغییرهای چندزمینهای غالباً نشانهٔ آناند که مرزها بهدرستی طراحی نشدهاند. از سوی دیگر، اصلاح مرزهای فیزیکی زمینههای محدود نیز پرهزینه است؛ بنابراین مرزهای نامناسب ممکن است دستنخورده باقی بمانند و به بدهی فنی تبدیل شوند.
مرزهایی که بعداً نامعتبر میشوند معمولاً در جایی پدید میآیند که دامنهٔ کسبوکار هنوز خوب شناخته نشده یا نیازمندیها با فرکانس بالا تغییر میکنند. نوسان و عدمقطعیت از ویژگیهای دامنههای فرعی هستهای است. از این مشاهده میتوان یک راهنما استخراج کرد: هنگام طراحی زمینههای محدود، بهویژه پیرامون دامنههای هستهای، ابتدا با مرزهای عریضتر شروع کنید. مرزهای منطقی را بسیار ارزانتر از مرزهای فیزیکی میتوان جابهجا کرد. با افزایش دانش دامنه و تثبیت نیازمندیها، زمینهٔ عریض را در صورت نیاز به زمینههای کوچکتر تجزیه کنید.
این راهنما بیشتر برای زمینههایی صادق است که دامنهٔ هستهای دارند؛ دامنههای عمومی و پشتیبان معمولاً استانداردتر و کمنوسانترند. برای حفاظت از خود در برابر تغییرهای پیشبینینشده، میتوان در زمینهای که دامنهٔ هستهای را در بر میگیرد، دامنههای دیگری را نیز که بیشترین تعامل را با آن دارند موقتاً در همان مرز نگه داشت؛ چه هستهای باشند، چه پشتیبان و چه عمومی.
الگوهای پیادهسازی منطق کسبوکار
در فصلهای ۵ تا ۷ چهار روش اصلی برای مدلسازی منطق کسبوکار را دیدیم: Transaction Script، Active Record، Domain Model و Event-Sourced Domain Model.
- Transaction Script و Active Record برای منطق کسبوکار ساده مناسبترند؛ مثلاً دامنههای پشتیبان یا یکپارچهسازی راهحل آماده در یک دامنهٔ عمومی.
- تفاوت اصلی آن دو در پیچیدگی ساختار داده است: Transaction Script برای ساختارهای ساده خوب است و Active Record پیچیدگی نگاشت ساختارهای دادهٔ پیچیدهتر به پایگاه داده را کپسوله میکند.
- Domain Model و گونهٔ رویدادمنبع آن برای دامنههای دارای منطق پیچیده، یعنی معمولاً دامنههای هستهای، مناسباند.
- اگر دامنه با تراکنشهای پولی سروکار دارد، بهطور قانونی به گزارش ممیزی سازگار نیاز دارد یا تحلیل عمیق رفتار سیستم از نظر کسبوکار ضروری است، Event-Sourced Domain Model انتخاب مناسبتری است.
با کنار هم گذاشتن این مشاهدات، میتوان انتخاب الگوی منطق کسبوکار را به یک درخت تصمیم تبدیل کرد:
- آیا دامنه پول یا تراکنش مالی را دنبال میکند، به گزارش ممیزی کاملاً سازگار نیاز دارد، یا کسبوکار به تحلیل عمیق رفتار آن نیازمند است؟ اگر بله، از Event-Sourced Domain Model استفاده کنید.
- اگر نه، آیا منطق کسبوکار پیچیده است؟ اگر بله، از Domain Model استفاده کنید.
- اگر نه، آیا ساختارهای داده پیچیدهاند؟ اگر بله، Active Record را انتخاب کنید.
- در غیر این صورت، Transaction Script کافی است.
مرز میان منطق ساده و پیچیده کاملاً تیز نیست. بهطور کلی، منطق پیچیده شامل قوانین کسبوکار، ناورداییها و الگوریتمهای پیچیده است، در حالی که منطق ساده بیشتر حول اعتبارسنجی ورودیها میگردد. راهنمای دیگر، پیچیدگی خود زبان فراگیر است: آیا زبان دامنه عمدتاً عملیات CRUD را بیان میکند یا دربارهٔ فرایندها و قوانین پیچیدهٔ کسبوکار صحبت میکند؟
انتخاب الگو بر اساس پیچیدگی منطق و داده، ابزار خوبی برای بازبینی فرض ما دربارهٔ نوع دامنهٔ فرعی نیز هست. اگر چیزی را هستهای میدانید اما Active Record یا Transaction Script بهترین الگوی آن است، یا دامنهای را پشتیبان میدانید اما تنها با Domain Model یا Event-Sourced Domain Model قابل مدیریت است، زمان خوبی برای بازبینی درکتان از دامنه و ارزش تجاری آن است. مزیت رقابتی دامنهٔ هستهای لزوماً فنی نیست.
الگوهای معماری
در فصل ۸ سه الگوی معماری لایهای، Ports & Adapters و CQRS را بررسی کردیم. وقتی الگوی منطق کسبوکار معلوم باشد، انتخاب معماری تا حد زیادی مستقیم میشود:
- Event-Sourced Domain Model → CQRS: بدون CQRS گزینههای پرسوجو بسیار محدود میشوند و عملاً فقط میتوان یک نمونه را با شناسه بازیابی کرد.
- Domain Model → Ports & Adapters: معماری لایهای باعث میشود مستقل نگه داشتن Aggregateها و Value Objectها از جزئیات Persistence دشوار شود.
- Active Record → Layered Architecture + Application/Service Layer: لایهٔ کاربردی منطق هماهنگکنندهٔ Active Recordها را بر عهده میگیرد.
- Transaction Script → معماری لایهای حداقلی سهلایه.
استثنا CQRS است. CQRS علاوه بر Event-Sourced Domain Model، هرجا که نیاز باشد یک داده در چند مدل پایدار متفاوت نمایش داده شود میتواند مفید باشد.
راهبرد آزمون
شناخت الگوی منطق کسبوکار و معماری میتواند برای انتخاب راهبرد آزمون نیز بهعنوان راهنما استفاده شود. سه راهبرد اصلی عبارتاند از:
هرم آزمون (Testing Pyramid)
هرم کلاسیک آزمون بر تعداد زیاد آزمون واحد، تعداد کمتر آزمون یکپارچهسازی و تعداد بسیار کمتر آزمون سرتاسری تأکید دارد. هر دو گونهٔ Domain Model با این راهبرد بهخوبی همخواناند؛ Aggregateها و Value Objectها واحدهای طبیعی و مؤثری برای آزمون منطق کسبوکار هستند.
الماس آزمون (Testing Diamond)
الماس آزمون بیشترین تمرکز را روی آزمونهای یکپارچهسازی قرار میدهد. در Active Record، منطق کسبوکار بنا بر تعریف میان لایهٔ سرویس و لایهٔ منطق کسبوکار پخش است؛ بنابراین آزمون یکپارچگی این دو لایه اهمیت ویژه دارد. متن منبع در همین بند عبارت «testing pyramid is the more effective choice» را به کار برده است؛ این عبارت بدون اصلاح محتوایی حفظ شده، هرچند عنوان بخش Testing Diamond است.
هرم آزمون معکوس (Reversed Testing Pyramid)
در این راهبرد بیشترین تأکید بر آزمونهای سرتاسری است؛ یعنی اعتبارسنجی گردش کار برنامه از ابتدا تا انتها. این روش برای کدهایی که Transaction Script دارند مناسب است، چون منطق کسبوکار ساده و تعداد لایهها حداقلی است و آزمون جریان کامل سیستم ارزش بیشتری دارد.
درخت تصمیم طراحی تاکتیکی
راهنماهای انتخاب الگوی منطق کسبوکار، معماری و راهبرد آزمون را میتوان در یک درخت تصمیم واحد ترکیب کرد. شناسایی نوع دامنههای فرعی و حرکت روی این درخت، نقطهٔ شروع محکمی برای تصمیمهای اساسی طراحی فراهم میکند؛ اما اینها قانون قطعی نیستند. تقریباً برای هر قانون استثنا وجود دارد، چه رسد به heuristic که اساساً قرار نیست در صددرصد موارد درست باشد.
این درخت بر ترجیح نویسنده برای استفاده از ابزارهای ساده و رجوع به الگوهای پیشرفته—Domain Model، Event-Sourced Domain Model، CQRS و غیره—تنها در صورت ضرورت بنا شده است. در عین حال، تیمهایی هستند که تجربهٔ فراوانی در مدل رویدادمنبع دارند و استفاده از آن در همهٔ دامنهها برایشان از ترکیب چند الگو سادهتر است. این رویکرد را نمیتوان برای همه توصیه کرد. در تجربهٔ نویسنده، انتخاب مبتنی بر heuristic معمولاً کارآمدتر از اعمال یک راهحل واحد به همهٔ مسئلهها بوده است.
در نهایت پاسخ به زمینهٔ خاص شما وابسته است. از درخت تصمیم و راهنماهای آن برای جهتگیری استفاده کنید، نه بهعنوان جایگزین تفکر نقادانه. اگر heuristic دیگری با زمینهٔ شما بهتر سازگار است، اصول راهنما را تغییر دهید یا درخت تصمیم خودتان را بسازید.
نتیجهگیری
این فصل بخشهای اول و دوم کتاب را در قالب یک چارچوب تصمیمگیری اکتشافی به هم متصل کرد. دیدیم چگونه دانش دامنه و دامنههای فرعی میتواند تصمیمهای فنی را هدایت کند: انتخاب مرزهای امن زمینههای محدود، مدلسازی منطق کسبوکار و تعیین الگویی معماری برای هماهنگسازی اجزای داخلی هر زمینه. همچنین از همان چارچوب برای اولویتبندی انواع آزمون بر اساس ماهیت دامنه استفاده کردیم.
اتخاذ تصمیم طراحی مهم است، اما مهمتر از آن اعتبارسنجی این تصمیمها در طول زمان است. فصل بعدی به مرحلهٔ بعدی چرخهٔ عمر طراحی نرمافزار میپردازد: تکامل تصمیمهای طراحی.
تمرینها
- فرض کنید سیستم مدیریت چرخهٔ عمر تیکت WolfDesk را پیادهسازی میکنید. این یک دامنهٔ هستهای است و برای بهینهسازی مداوم الگوریتم به تحلیل عمیق رفتار آن نیاز است. راهبرد اولیهٔ شما برای پیادهسازی منطق کسبوکار و معماری مؤلفه چیست؟ راهبرد آزمون چه خواهد بود؟
- برای ماژول مدیریت شیفت مأموران پشتیبانی WolfDesk چه تصمیمهای طراحی میگیرید؟
- برای آسانتر کردن مدیریت شیفتها، میخواهید از یک ارائهدهندهٔ خارجی تعطیلات عمومی مناطق جغرافیایی استفاده کنید. سیستم بهصورت دورهای ارائهدهنده را فراخوانی کرده و تاریخ و نام تعطیلات آینده را میگیرد. برای این یکپارچهسازی از چه منطق کسبوکار و الگوهای معماری استفاده میکنید و چگونه آن را آزمون میکنید؟
- بر اساس تجربهٔ خود، چه جنبههای دیگری از فرایند توسعهٔ نرمافزار را میتوان به درخت تصمیم heuristic این فصل افزود؟