وجود تطبيق واحد لا يعني أن كل أجزائه يجب أن تعرف تفاصيل بعضها. ووجود خدمات كثيرة لا يضمن فصل المسؤوليات. لفريق يبني منتجًا متغيّرًا، يمكن أن يكون Modular Monolith نقطة بداية عملية: نشر واحد، مع وحدات تملك قراراتها وواجهات واضحة بينها. هذا اختيار تصميمي يحتاج انضباطًا، وليس وعدًا بأن التقسيم سيحدث تلقائيًا.
ابدأ بحدود العمل
في منتج عضويات، توجد مفاهيم مثل الخطط والاشتراكات والمدفوعات والاستحقاقات. لا تقسّم الوحدات إلى controllers وservices وrepositories فقط؛ هذه طبقات تقنية داخل الوحدة، ولا توضّح من يملك قاعدة العمل. اسأل: من يقرّر أن العضوية فعّالة؟ ومن يثبت أن دفعة محدّدة ناجحة؟ الإجابة تحدّد الملكية واتجاه الاعتماد.
صمّم عقدًا بين الوحدات
يمكن لوحدة العضويات أن تطلب نتيجة دفعة موثوقة عبر واجهة، لكنها لا تعدّل سجل الدفع مباشرة. وبالمقابل لا ينبغي لبوابة الدفع أن تعرف تفاصيل توزيع مزايا كل خطة. يتيح NestJS تجميع providers وcontrollers داخل modules وتصدير ما يحتاجه الآخرون؛ لكن حدود المنتج نفسها مسؤولية التصميم وليست مجرد وضع الملفات في مجلد.
Payments -> PaymentConfirmed contract -> Memberships
Memberships -> EntitlementGranted contract -> Benefits
Notifications consumes outcomes; it does not decide eligibility.افصل القرار عن الأثر الجانبي
تفعيل العضوية قرار عمل، وإرسال رسالة ترحيب أثر جانبي. إذا فشل البريد، لا ينبغي إلغاء دفعة صحيحة أو ترك العضوية بحالة مبهمة. اكتب الانتقال التجاري أولًا، ثم نفّذ الإشعارات بمسار يمكن إعادة محاولته. عندما يتطلّب الحدث ضمان تسليم، استخدم سجلًا دائمًا مناسبًا بدل الاعتماد على callback في الذاكرة فقط.
امنع الاعتماد الدائري مبكرًا
إذا احتاجت وحدة الطلبات استيراد كل شيء من المدفوعات، والمدفوعات تستورد الطلبات، راجع المسؤوليات. أحيانًا تحتاج عملية تنسيق مستقلة تدير الحالة عبر عقود صغيرة. لا تُنشئ مجلد shared ضخمًا لحل كل دورة؛ قد ينقل الاقتران إلى مكان أقل وضوحًا. شارك أنواع العقود والأدوات العامة فقط، مع إبقاء قواعد العمل لدى مالكها.
اختبر الحدود بدل تفاصيل التنفيذ
اختبر الوحدة عبر واجهتها العامة: دفعة مؤكدة تُفعّل الاستحقاق مرة واحدة، وفشل الرسالة لا يغيّر النتيجة التجارية. أضف فحصًا يمنع الاستيراد من الملفات الداخلية للوحدات الأخرى إن كانت أدوات المشروع تدعمه. راقب حجم التغييرات المتقاطعة: ميزة بسيطة تتطلّب تعديل معظم الوحدات قد تكشف أن الحدود غير مناسبة.
متى تفصل خدمة فعلًا؟
عندما يظهر سبب قابل للقياس، مثل نمط توسّع مختلف أو ملكية فريق مستقلة أو حاجة عزل أعطال محددة، قيّم استخراج الوحدة. احسب تكلفة الشبكة والهوية والتتبّع وتوافق العقود وتشغيل نسخ متعددة. الحدود المنظّمة تجعل هذا الخيار ممكنًا، لكنها لا تلزمك به. الهدف أن يخدم التصميم سرعة التطوير وموثوقية المنتج بحجمه الفعلي.
سيناريو عملي: إضافة مزايا لخطة عضوية
إذا تغيّرت مزايا الخطة، يجب ألا يحتاج adapter الدفع إلى تعديل. وحدة الخطط تصف المزايا، ووحدة العضوية تحدّد الاستحقاق، ووحدة استخدام المزايا تنفّذ القواعد عند الاستهلاك. يمكن حفظ نسخة من شروط الاشتراك عندما يحتاج المنتج الحفاظ على اتفاق قديم، بدل قراءة الخطة الحالية دائمًا. هذه المسألة قرار عمل يجب توضيحه قبل تحويل العلاقات إلى استدعاءات بين services.
إشارة مبكرة إلى حدود غير مناسبة
راقب ميزات الأسبوع العادية: إذا كانت إضافة حقل عرض تتطلّب تغيير الدفع والإشعارات وقواعد العضوية معًا، افحص ما إذا كان هناك object مشترك يحمل تفاصيل أكثر من اللازم. قلّص العقد إلى ما يحتاجه المستهلك فعلًا. لا تقِس نجاح التقسيم بعدد المجلدات، بل بعدد القرارات التي يمكن تغييرها بأمان دون معرفة تفاصيل الوحدات الأخرى.
المراجع الرسمية
تدعم هذه المراجع سلوك الأدوات المذكورة. الأمثلة وقرارات التصميم توضيحية، ويجب تكييفها مع متطلبات المشروع وإصداراته.
إعداد: Noor Yasser
تعمل على تحدٍ تقني مشابه؟
أساعد الفرق على تحويل القرار المعماري إلى نطاق واضح وتنفيذ يمكن تشغيله ومراجعته بثقة.




