Software Architecture

إتقان التصميم الموجه للنطاق: دليل عملي لنمذجة منطق الأعمال المعقد

تنمو أنظمة البرمجيات. ومع توسعها، تزداد تعقيدات منطق الأعمال الذي يجب دعمه. عندما تتشابك قواعد الأعمال مع تفاصيل التنفيذ التقني، تتحول قواعد الشفرة إلى فوضى هشة وغير قابلة للصيانة. هنا يأتي دور التصميم الموجه للنطاق (DDD). ليس DDD مجرد مجموعة من الأنماط؛ إنه عقلية تعطي الأولوية للنطاق الأساسي ومنطق النطاق، وبناء هياكل معقدة بناءً على فهم عميق للأعمال.

اللغة الشاملة: سد الفجوة

قبل الغوص في الهياكل التقنية، يجب علينا إنشاء الجانب الأكثر أهمية في DDD: اللغة الشاملة. هذه لغة مشتركة يستخدمها كل من المطورين وخبراء النطاق (أصحاب المصلحة في الأعمال). إنها تقضي على أخطاء الترجمة حيث تعني كلمة "طلب" شيئاً مختلفاً للمبيعات وشيئاً آخر لخدمات اللوجستيات.

من خلال ضمان مطابقة أسماء الكود للمصطلحات التجارية، ننشئ توثيقاً حياً يظل ذا صلة. إذا تغير مصطلح تجاري، فمن المرجح أن يتغير الكود معه.

السياقات المحدودة: إدارة التعقيد على نطاق واسع

مع نمو النطاقات، غالباً ما تصبح اللغة الشاملة الواحدة غير كافية. هنا يأتي دور السياقات المحدودة. يحدد السياق المحدد الحدود التي ينطبق فيها نموذج معين وتكون اللغة الشاملة صالحة. قد يكون لنفس المفهوم معانٍ مختلفة في سياقات مختلفة. على سبيل المثال، قد يشير "الحساب" في سياق مصرفي إلى ملف تعريف العميل، بينما في سياق الفوترة، يشير إلى قيد في دفتر الأستاذ المالي.

يسمح فصل هذه السياقات للفرق بالعمل بشكل مستقل واختيار الحلول التقنية الأكثر ملاءمة لكل نطاق فرعي.

التجمعات: حدود الاتساق

داخل السياق المحدد، ننظم نموذج النطاق الخاص بنا باستخدام التجمعات. التجمع هو مجموعة من الكائنات المرتبطة التي نعاملها كوحدة واحدة لتغييرات البيانات. يتكون من جذع التجمع والكائنات الداخلية الخاصة به.

القاعدة الرئيسية هي: يمكن للكائنات الخارجية الاحتفاظ بالإشارات إلى جذع التجمع فقط، وليس إلى الكائنات الداخلية. يضمن ذلك سلامة المعاملات والاتساق. عند تحديث تجمع، تقوم بتحديثه ككل.

مثال: تجمع طلب


public class Order {
    private List items;
    private CustomerId customerId;
    private Money totalAmount;

    public void addItem(ProductId productId, int quantity) {
        // منطق الأعمال لإضافة عنصر
        this.items.add(new OrderItem(productId, quantity));
        this.recalculateTotal();
    }

    public void cancel() {
        if (this.items.isEmpty()) {
            throw new IllegalStateException("لا يمكن إلغاء طلب فارغ");
        }
        // تشغيل حدث النطاق
        this.raiseEvent(new OrderCanceledEvent(this.id));
    }

    private void recalculateTotal() {
        this.totalAmount = this.items.stream()
            .map(item -> item.getPrice().multiply(item.getQuantity()))
            .reduce(Money.ZERO, Money::add);
    }
}

في هذا المثال، Order هو جذع التجمع. OrderItem هو كيان داخل التجمع. لاحظ كيف يفرض طريقة cancel قواعد الأعمال داخلياً، مما يحمي اتساق التجمع.

الكيانات مقابل كائنات القيمة

التمييز الحاسم في DDD هو بين الكيانات وكائنات القيمة:

  • الكيانات لها هوية فريدة تستمر مع مرور الوقت، حتى إذا تغيرت سماتها (مثل معرف الطلب).
  • كائنات القيمة غير قابلة للتغيير وتُعرّف بسماتها. تكون كائنان للقيمة متساويين إذا كانت جميع خصائصهما متساوية (مثل كائن Money أو EmailAddress).

يستخدم كائنات القيمة للخصائص مثل المال أو العناوين لمنع الحالات غير الصالحة ويجعل نموذج النطاق أكثر ثراءً وتعبيراً.

أحداث النطاق: فصل الآثار الجانبية

غالباً ما تتطلب النطاقات المعقدة آثاراً جانبية، مثل إرسال بريد إلكتروني أو تحديث عدد المخزون. بدلاً من ربط هذه الإجراءات مباشرة بمنطق النطاق، نستخدم أحداث النطاق. هذه إشعارات بأن شيئاً مهماً قد حدث في النطاق.

في مثال الكود أعلاه، رأينا raiseEvent(new OrderCanceledEvent(this.id)). يفصل هذا فعل الإلغاء عن عملية الإشعار. يمكن لمعالج الأحداث منفصل الاستماع إلى OrderCanceled وتنفيذ الإجراءات اللاحقة الضرورية دون إرباك تجمع Order.

الخاتمة

يعد التصميم الموجه للنطاق نهجاً قوياً لبناء أنظمة برمجية معقدة. من خلال التركيز على السياقات المحددة، وإنفاذ اتساق التجمعات، والاستفادة من الكيانات وكائنات القيمة، واستخدام أحداث النطاق، يمكننا إنشاء أنظمة ليست قوية تقنياً فحسب، بل متوافقة أيضاً بعمق مع أهداف الأعمال. وعلى الرغم من أن منحنى التعلم حاد، فإن الفوائد طويلة الأجل للصيانة والقابلية للتوسع والتواصل الواضح بين الفرق التقنية وفريق الأعمال لا تقدر بثمن.

Share: