یک تغییر در Billing:
➡️ نیاز به Recompile
➡️ نیاز به Retest
➡️ نیاز به Redeploy
برای Shipping خواهد داشت.
شما دو Bounded Context را که قرار بود مستقل باشند، فقط برای صرفهجویی در چند Property به هم جوش دادهاید.
داشتن دو مدل Order مستقل دقیقاً همان دلیلی است که دادهها باید داخل مرزهای خودشان باقی بمانند.
شباهت داشتن آنها کاملاً طبیعی است.
آنها یک مفهوم واقعی را از دو زاویه متفاوت مدل میکنند؛ زاویههایی که به مرور زمان از هم فاصله خواهند گرفت.
📏 قانونی که من استفاده میکنم: تا بار سوم صبر کن
من بار دوم چیزی را Deduplicate نمیکنم.
تا بار سوم صبر میکنم و فقط یک سؤال میپرسم:
اگر این قانون تغییر کند، آیا هر دو نسخه باید با هم تغییر کنند؟
اگر پاسخ بله باشد:
✅ با تکرار واقعی روبهرو هستید.
✅ همان حقیقت در چند نقطه نوشته شده است.
✅ آن را استخراج کنید.
اگر پاسخ خیر باشد:
✅ شباهت فقط تصادفی است.
✅ آن را به حال خودش رها کنید.
✅ ءCoupling آینده هزینه بیشتری خواهد داشت.
اجازه دهید کد تکرار شود تا زمانی که Abstraction مناسب خودش را نشان دهد.
ءAbstractionهای خوب از مثالهای واقعی کشف میشوند، نه اینکه از ابتدا حدس زده شوند.
بعضی افراد این رویکرد را AHA (Avoid Hasty Abstractions) مینامند.
💡 یک نشانه عملی
وقتی بتوانید مفهوم را نامگذاری کنید، زمان استخراج فرا رسیده است.
نامهایی مثل:
✅
Money✅
TaxRate✅
InvoiceNumberاحتمالاً دانش دامنه هستند و ارزش تبدیل شدن به Value Object را دارند.
اما اگر بهترین نامی که پیدا میکنید اینها باشد:
❌
Helper❌
Utils❌
ProcessDataاحتمالاً در حال Abstract کردن شکل کد هستید، نه دانش.
✅ زمانی که DRY واقعاً درست است
وقتی درست استفاده شود، DRY فوقالعاده ارزشمند است.
یک قانون کسبوکار باید فقط در یک مکان وجود داشته باشد.
فرض کنید قانون زیر را در سه بخش مختلف کپی کردهایم:
«سفارشهای بالاتر از ۱۰۰۰ دلار نیاز به تأیید مدیر دارند.»
// OrderService
if (order.Total > 1000) { /* require approval */ }
// CheckoutService
if (order.Total > 1000m) { /* require approval */ }
// AdminController - someone bumped the limit here, and only here
if (order.Total > 5000) { /* require approval */ }
بالاخره روزی دو مورد را تغییر میدهید و سومی را فراموش میکنید.
همین نسخه سومِ منحرفشده منشأ باگ خواهد بود.
قانون را به Domain Model منتقل کنید تا تنها یک خانه داشته باشد:
public bool RequiresManagerApproval() => Total > 1000;
این دقیقاً همان «نمایش یکتای مرجع» است که DRY درباره آن صحبت میکند.
📝 جمعبندی
🔹 ءDRY درباره دانش است، نه کدهایی که شبیه هم هستند.
🔹 هزینه یک Abstraction اشتباه از هزینه Duplication بیشتر است و حذف آن نیز سختتر خواهد بود.
🔹 تا بار سوم صبر کنید. فقط زمانی استخراج کنید که هر دو نسخه یک حقیقت مشترک را نمایش میدهند و باید با هم تغییر کنند.
🔹 دفعه بعد که خواستید یک کد تکراری را حذف کنید، از خودتان نپرسید:
«آیا این دو قطعه کد شبیه هم هستند؟»
بپرسید:
«آیا معنای یکسانی دارند؟»
همین یک سؤال، دردسرهای نگهداری بیشتری را از شما دور خواهد کرد تا هر مقدار تایپ نکردنی که DRY برایتان ذخیره میکند. 🚀
🔖هشتگها:
#cleanarchitecture #dry #dotnetdeveloper