الـ Modular Monolith
بيتعمل إزاي؟ (بـ Folder Structure + Diagram + شرح مصطلحات)
أولًا: الـ Folder Structure (نضيف وعالوضع)
نفترض إننا شغالين على سيستم E-Commerce بسيط:
/src
├─ /BuildingBlocks (الأساسيات اللي الكل بيستخدمها)
│ ├─ /Messaging (شغل الربط)
│ │ ├─ IEvent.cs
│ │ ├─ IEventBus.cs
│ │ └─ OutboxMessage.cs
│ ├─ /Domain (القواعد العامة)
│ │ └─ Entity.cs
│ └─ /Infrastructure (التعامل مع الداتا)
│ └─ DbContextBase.cs
│
├─ /Modules (قلب السيستم)
│ ├─ /Orders (موديول الطلبات)
│ │ ├─ OrdersModule.cs
│ │ ├─ /Application (الـ Use Cases)
│ │ ├─ /Domain (الـ Logic بتاع الـ Orders)
│ │ │ ├─ Order.cs
│ │ │ └─ Events
│ │ │ └─ OrderPlacedEvent.cs
│ │ ├─ /Infrastructure (الـ Database الخاصة بالـ Orders)
│ │ │ └─ OrdersDbContext.cs
│ │ └─ /API (الـ Controllers)
│ │
│ ├─ /Inventory (موديول المخازن)
│ │ ├─ InventoryModule.cs
│ │ └─ ... (نفس التقسيمة)
│ │
│ └─ /Notifications (موديول التنبيهات)
│ └─ ... (نفس التقسيمة)
│
└─ /API (الـ Entry Point)
└─ Program.cs
الفكرة هنا:
كل موديول "عايش مع نفسه": عنده الـ Domain والـ Application والـ Infrastructure بتوعه، شايل داته في جيبه، ومحدش يدخل له إلا من الباب الرسمي.
ثانياً: Diagram يوضح "مين بيكلم مين"
الفكرة:
الـ Orders مش مستنية الـ Inventory، ولا تعرف حاجة عن الـ Notifications.. كله شغال Event-Driven وجوه نفس الـ App (يعني أداء عالي وفي نفس الوقت نظام محترم).
ثالثاً: الـ Arch Tests (يعني إيه؟)
المقصود بيها اختبارات المعمارية.. دي مش بتختبر الـ Business شغال صح ولا لأ، دي بتختبر "النظام" نفسه.
يعني بنسأل: "هل الموديولات محترمة حدودها ولا ضاربين في بعض؟"
مثال بـ NetArchTest:
[Test]
public void Orders_Should_Not_Depend_On_Inventory()
{
var result = Types.InAssembly(typeof(OrdersModule).Assembly)
.That()
.ResideInNamespace("Modules.Orders")
.ShouldNot()
.HaveDependencyOn("Modules.Inventory")
.GetResult();
Assert.IsTrue(result.IsSuccessful);
}
ده بيعمل إيه؟
لو أي مبرمج "استسهل" وعمل using لموديول الـ Inventory جوه الـ Orders.. الـ Build يضرب في وشه ❌. ده اسمه Enforcement (فرض سيطرة) مش مجرد نصيحة.
رابعاً: يعني إيه Side Effects؟
ببساطة: أي حاجة مش هي "الهدف الأساسي" من العملية اللي بتعملها دلوقتي.
الهدف الأساسي: إنك تعمل Order (الطلب يتم).
الـ Side Effects:
تحجز المخزن (Inventory).
تبعت إيميل (Email).
تحدث تقارير البيع (Analytics).
ليه بنفصلهم؟ عشان الـ Order يكمل بنجاح حتى لو سيرفر الإيميل واقع أو موديول التحليلات مهنج. الـ Side Effects بنرميها Events وننساها.
خامساً: يعني إيه Integrations؟
المقصود بيها أي تعامل مع "عالم بره" السيستم بتاعك:
بوابة دفع (Payment Gateway).
شركة شحن (Shipping).
خدمة SMS.
ليه بنعملها Async؟
لأن الأنظمة دي "غدارة": بطيئة، ممكن تقع، ملكش كلمة عليها.
بدل ما تخلي اليوزر مستني Order -> Payment -> Shipping.. لا، إنت خلص الـ Order وارمي Event، والـ Integration موديول يتعامل براحته ويعمل Retry لو السيستم اللي بره واقع.
سادساً: "لما تحب تعزل الأعطال" (Fault Isolation)
السيناريو الكلاسيكي المرعب: موديول المخازن (Inventory) هنج، فالـ Order مستني رد، فالـ Thread محبوس، فاليوزر مستني والصفحة بتلف.. وفي الآخر السيستم كله يقع! 💥
الحل في الـ Modular Monolith:
الـ Order بيسجل الطلب ويرمي Event ويقول لليوزر "تمام".
الـ Inventory مات؟ مش مشكلة، الـ Event مركون في الـ Queue.
أول ما الـ Inventory "يفوق"، يسحب الـ Event ويكمل شغله.
كده العطل "اتحبس" في مكانه وموتش السيستم كله.
الخلاصة الذهبية 🥇
الـ Modular Monolith الصح يعني:
تقسيم حسب الـ Business (مش بمزاجنا).
Folder Structure واضح ومحدد.
كل موديول له باب واحد (Public API).
تواصل عن طريق الـ Events.
اختبارات (Arch Tests) تضمن إن مفيش موديول يسرح على التاني.
Comments
Post a Comment