سؤال

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

لإضافة طلب إلى مستودع ، عادةً ما أرى الأشخاص في وضع إنشاء ترتيب بدون معرف ثم جعل المستودع يكمل الكائن:

class OrderRepository
{
    void Add(Order order)
    {
        // Insert order into db and populate Id of new order
    }
}

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

بدلاً من ذلك ، رأيت أيضًا هذا النهج:

class OrderRepository
{
    Order AddOrder(Customer customer)
        // It might be better to call this CreateOrder
    {
        // Insert record into db and return a new instance of Order
    }
}

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

في كلتا الحالتين ، يعمل سؤالي هو: هل يجب أن أعيش مع أحد هذين التفسيرين ، أم أن هناك أفضل ممارسة لتصميم الإدراج؟

لقد وجدت هذه الإجابة مماثلة ، ولكن بالنسبة لكائنات القيمة:كيف يمكنني إضافة كائن إلى مجموعة تحتفظ بها الجذر الكلي. عندما يتعلق الأمر بكائن القيمة ، لا يوجد أي ارتباك ، لكن سؤالي يتعلق بكيان مع المعرف المستمد من مصدر خارجي (معرّف قاعدة بيانات تم إنشاؤه تلقائيًا).

هل كانت مفيدة؟

المحلول

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

تعتمد الخيارات المتبقية على منطق المجال. إذا كان منطق المجال يملي أن أمرًا بدون معرف لا معنى له ، فإن المعرف هو ثابت مطلوب من الطلب ، ويجب أن نضعه في تصميمه:

public class Order
{
    private readonly int id;

    public Order(int id)
    {
        // consider a Guard Clause here if you have constraints on the ID
        this.id = id;
    }
}

لاحظ أنه من خلال وضع علامة id الحقل كما readonly لقد جعلناها ثابتة. لا توجد طريقة يمكننا من خلالها تغييره لمثيل ترتيب معين. هذا يناسب تماما مع تصميم يحركه المجالشخصية نمط.

يمكنك مزيد من فرض منطق المجال عن طريق وضع شرط الحراسة في المُنشئ لمنع المعرف من أن يكون سلبيًا أو صفرًا.

من المحتمل الآن أن تتساءل كيف سيعمل هذا مع معرفات تم إنشاؤها تلقائيًا من قاعدة بيانات. حسنًا ، لا.

لا توجد طريقة جيدة لضمان عدم استخدام المعرف المقدم بالفعل.

هذا يتركك بخيارين:

  • تغيير المعرف إلى GUID. هذا يسمح لأي متصل بتوفير معرف فريد لطلب جديد. ومع ذلك ، فإن هذا يتطلب منك استخدام GUIDS كمفاتيح قاعدة البيانات أيضًا.
  • قم بتغيير واجهة برمجة التطبيقات (API) بحيث لا يأخذ إنشاء طلب جديد كائن طلب ، بل orderrequest كما اقترحت - يمكن أن يكون OrderRequest متطابقًا تقريبًا مع فئة الطلب ، ناقص المعرف.

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

قد أذهب إلى حد القول إنهم لا يجب تكون ذات صلة ، لأن OrderRequest هو كائن القيمة في حين أن الطلب هو شخصية.

إذا تم اتخاذ هذا النهج ، يجب أن تقوم طريقة Addorder بإرجاع مثيل الطلب (أو على الأقل المعرف) ، لأنه وإلا لا يمكننا معرفة معرف الطلب الذي أنشأناه للتو. هذا يقودنا إلى انتهاك CQS وهذا هو السبب في أنني أميل إلى ذلك تفضل guids لمعرفات الكيان.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top