يجب أن أقوم بتعيين DTO إلى / من كيان مجال على الجانبين العميل والخادم؟

StackOverflow https://stackoverflow.com/questions/2201150

سؤال

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

يتحدث عميلي إلى الخادم فقط عبر WCF.

على هذا النحو، لكل كيان مجال، لدي DTO المقابلة - تمثيل بسيط يحتوي على بيانات فقط - بالإضافة إلى فئة MAPPER التي تنفذ DtoMapper<DTO,Entity> ويمكن تحويل كيان إلى ما يعادله DTO أو العكس من خلال بوابة ثابتة:

var employee = Map<Employee>.from_dto<EmployeeDto>();

الجانب الخادم من هذا التطبيق هو في الغالب حول الثبات، حيث تأتي DTOS من خدمة WCF، ويتم تشغيلها، ثم استمرت Orm التعسفي في قاعدة البيانات، أو يأتي طلب استعلام من WCF وتنفذ Orm هذا الاستعلام عن DB وإرجاع الكائنات المراد إرسالها وإرسالها بواسطة WCF.

بالنظر إلى هذا السيناريو، هل يجعل أي معنى لتعيين متجر الثبات الخاص بي إلى كيانات المجال، أو يجب أن أفعل مجرد خريطة مباشرة إلى DTOS؟

إذا كنت تستخدم كيانات المجال، فإن التدفق سيكون

  1. طلبات العميل كائن
  2. WCF ينقل طلب إلى الخادم
  3. قاعدة بيانات الاستفسارات orm وإرجاع كيانات النطاق
  4. تم تحويل كيانات المجال إلى DTOS بواسطة mapper
  5. Serializes WCF DTO والعودة إلى العميل
  6. العميل يحمل DTO.
  7. تحولت DTO إلى كيان المجال بواسطة mapper
  8. تم إنشاء ViewModels، إلخ.

مماثلة في رحلة العودة

إذا قمت بتعيين مباشرة إلى DTO، يمكنني القضاء على تعيين واحد لكل كائن، حسب الطلب. ماذا أخسر عن طريق القيام بذلك؟

الشيء الوحيد الذي يتبادر إلى الذهن هو فرصة أخرى للتحقق من صحة قبل إدراج / تحديث، لأنني ليس لدي أي ضمان أن DTO عرضة للتحقق من الصحة أو حتى موجودة ككيان مجال قبل إرساله عبر السلك، وأعتقد فرصة التحقق من الصحة عند تحديد (إذا كان قد تضع عملية أخرى قيم غير صالحة في قاعدة البيانات). هل هناك أسباب أخرى؟ هل هذه الأسباب كافية لتبرير خطوات التعيين الإضافية؟

تعديل:

لقد قلت "Orbitrary orm" أعلاه، وأريد أن تكون أشياء أن تكون غير ملائمة للأورم والثابتة قدر الإمكان، ولكن إذا كان لديك أي شيء خاص لإضافة هذا هو محددا ل Nhibernate، فكل الوسائل.

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

المحلول

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

النظر في ما هي خدمة ويب. انها ليست مجرد تجريد على orm الخاص بك؛ إنها عقد. وبعد إنها API العامة لعملائك، داخل الداخل والخارجي.

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

في الواقع ممارسة شائعة (لن أهان القراء مع عبارة "أفضل الممارسات") لإنشاء محددة Request و Response فصول لكل رسالة لسبب مشابه؛ يصبح أبسط بكثير لتمديد قدرة الخدمات والأساليب الموجودة دون كسر التغييرات.

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


وأحيانا، انها ليست كذلك المستطاع لإرسال النموذج الخاص بك مرة أخرى من خلال API. هناك العديد من الأسباب التي تجعل هذا يمكن أن يحدث:

  • دورات في الرسم البياني للكائن. بخير تماما في OOP؛ كارثية في التسلسل. ينتهي الأمر بالحاجة إلى اتخاذ خيارات دائمة مؤلمة حول "الاتجاه" يجب أن يتم تسجيل الرسم البياني "الاتجاه". من ناحية أخرى، إذا كنت تستخدم DTO، فيمكنك التسلسل بأي الاتجاه الذي تريده، مهما يناسب المهمة في متناول اليد.

  • قد تكون محاولة استخدام أنواع معينة من آليات الوراثة على الصابون / الراحة Kludge في أحسن الأحوال. تسلسل XML النمط القديم يدعم على الأقل xs:choice; DataContract لا، ولن أتعرض على الأساس المنطقي، لكنه يكفي أن أقول أنك ربما يكون لديك بعض الأشكال تعدد الأشكال في طراز المجال الغني الخاص بك ولعنة شبه مستحيل لتوجيه ذلك من خلال خدمة الويب.

  • تحميل كسول / تأجيل، والذي ربما تستخدمه إذا كنت تستخدم orm. من المحرج بما فيه الكفاية التأكد من أنها تستسجل بشكل صحيح - على سبيل المثال، باستخدام LinQ إلى كيانات SQL، لا يؤدي WCF إلى التحميل الكسول، كما أنه سيضع للتو null في هذا الحقل ما لم تقم بتحميله يدويا - ولكن المشكلة تزداد سوءا بالنسبة للبيانات التي تعود إليها. شيء بسيط مثل List<T> خاصية السيارات التي تتم تهيئتها في المنشئ - مشترك بما فيه الكفاية في نموذج المجال - ببساطة لا يعمل في WCF، لأنه لا يستدعي منشئك. بدلا من ذلك عليك إضافة [OnDeserializing] طريقة تهيئة، وأنت حقا لا تريد فوضى نموذج المجال الخاص بك مع هذه القمامة.

  • أنا أيضا لاحظت الملاحظة الأقواس التي تستخدمها. النظر في أن واجهات مثل IList<T> لا يمكن تسلسل في جميع أنحاء خدمة ويب! إذا كنت تستخدم فئات Poco مع Nhibernate، لأن معظمنا، فهذا ببساطة لن يعمل، الفترة.


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

  • معلومات حول الحساب (رقم الحساب، الاسم، إلخ)
  • البيانات الخاصة بالفاتورة (رقم الفاتورة، التاريخ، تاريخ الاستحقاق، إلخ)
  • معلومات المستوى A / R (الرصيد السابق، الرسوم المتأخرة، توازن جديد)
  • معلومات المنتج أو الخدمة لكل شيء على الفاتورة؛
  • إلخ.

هذا ربما يناسب بشكل جيد داخل نموذج المجال. ولكن ماذا لو كان العميل يريد تشغيل تقرير يظهر 1200 من هذه الفواتير؟ نوع من تقرير المصالحة؟

هذا تمتص التسلسل. أنت الآن ترسل 1200 فواتير مع نفس البيانات التي يتم تسليمها مرارا وتكرارا - نفس الحسابات، نفس المنتجات، نفس A / R. داخليا، والتطبيق الخاص بك هو تتبع جميع الروابط؛ إنه يعرف الفاتورة # 35 وفاتورة # 45 لنفس العميل وبالتالي شارك Customer المرجعي؛ يتم فقد جميع هذه المعلومات عند التسلسل وينتهي بك الأمر بإرسال كمية سخيفة من البيانات الزائدة.

ما تريده حقا هو إرسال تقرير مخصص يتضمن:

  • جميع الحسابات المدرجة في التقرير، و A / R؛
  • جميع المنتجات المدرجة في التقرير؛
  • جميع الفواتير، مع معرفات المنتج والحساب فقط.

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

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

نصائح أخرى

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

طبقات التجريد جيدة ليس فقط للتحقق من الصحة، ولكن عزل التعليمات البرمجية الخاصة بك من التغييرات أدناه / فوقها، وجعل الشفرة أكثر قابلية للتحقق ومع التكرار أقل.

أيضا، ما لم تلاحظ اختناق أداء كبير في التحويل الإضافي، تذكر: التحسين المبكر هو أصل كل الشر. :)

يجب عليك بالتأكيد الحفاظ على كيانات النطاق الخاصة بك منفصلة عن DTO الخاص بك فهي مخاوف مختلفة. عادة ما تكون DTO عادة نماذج ووصفا ذاتيا حيث تكون كيانات المجال الخاصة بك من ناحية أخرى تغليف منطق عملك ولديك الكثير من السلوك المرفقة معهم.

بعد القول أنني لست متأكدا من أين التعيين الإضافي؟ تقوم باسترداد البيانات باستخدام ORM (كيانات المجال AKA) وأنت تقوم بتعيين هذه الكائنات إلى DTTO الخاصة بك حتى يكون هناك فقط رسم خرائط هناك؟ راجع للشغل إذا كنت لا تستخدم بالفعل شيء مثل Automapper. للقيام رسم الخرائط المملة لك.

يتم بعد ذلك التحمل بعد ذلك نفس DTO على العميل ومن هناك يمكنك تعيين مباشرة إلى Uiviewmodels الخاص بك. لذلك تبدو الصورة الكبيرة شيئا مثل:

  • كيان طلبات العميل حسب معرف خدمة WCF
  • خدمة WCF يحصل على كيان من مستودع / أورم
  • يستخدم Automapper to الخريطة من كيان إلى DTO
  • يتلقى العميل DTO.
  • يستخدم Automapper للخريطة إلى UI ViewModel
  • يتم ربط uiviewmodel لوجوي

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

أيضا، اعتبر ما إذا كان DTO مصمم لمجال العميل.
هل هذا المجال العميل الوحيد الذي يحتاج إلى الوصول إلى متجر البيانات هذا عبر خدمتك؟
هل DTOS جانب الخادم مرنة أو حبيبات خشنة بما يكفي لخدمة مجال تطبيق مختلف؟
إذا لم يكن الأمر كذلك، فمن المحتمل أن يستحق الجهد المبذول للحفاظ على تطبيقات الواجهة الخارجية المستخرجة.

(DB-> Orm-> موظف -> client1dtoassembler-> client1mplayeedto).

لدينا تطبيق مماثل حيث تعمل خدمة WCF في المقام الأول كعبارة إلى متجر البيانات المستمر.

في حالتنا، لا تعيد عميلنا وخادمنا إعادة استخدام الجمعية التي تحتوي على "DTOS". هذا يعطينا الفرصة لإضافة رمز ببساطة إلى الفصول الجزئية التي تم إنشاؤها بواسطة مرجع الخدمة، لذلك نحن في كثير من الأحيان قادرون على استخدام DTTO AS-IS على جانب العميل وعلاجها ككائن مجال. في بعض الأحيان قد يكون لدينا كائنات مجال من جانب العميل التي تخدم كواجهات إلى مجموعة من الكائنات المستمرة التي حصلنا عليها من خدمة WCF.

عندما تفكر في السلوكيات والخصائص المحسوبة التي تحتوي كائنات المجال الخاصة بك، ما مقدار التداخل هناك، حقا بين عميلك وخادمك؟ في حالتنا، قررنا أن تقسيم المسؤوليات بين العميل والخادم يعني أنه كان هناك القليل جدا، إن وجد رمز يحتاج إلى حاضر (والضبط نفسه) على كل من العميل والخادم.

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

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

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

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

أحب أن أبقيها بسيطة وإعادة العامل حسب الحاجة لاحقا، حاول تجنب شيء التحسين ما قبل الناضجة، إلخ.

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