Правильная архитектура: добавление атрибутов в модель предметной области в .NET

Задний план

Мне нравится модель Jeffrey Palermo Onion Architecture (похожая на Гексагональная архитектура), предписывающая, чтобы Модель предметной области находилась в центре внимания, и конкретные реализации инфраструктуры, в частности Конкретные репозитории быть на периферии.

введите здесь описание изображения

Допустим, у меня есть модель домена:

//https://libphonenumber.codeplex.com/
using libphonenumber;

namespace MyApplication.Domain
{
    public class Speaker
    {
         public virtual string Name {get;set;}
         public virtual PhoneNumber PhoneNumber {get;set;}
    }
}

Теперь мне нужно показать эту модель предметной области другим командам:

  • Команда пользовательского интерфейса гипотетически хочет добавить несколько атрибутов проверки данных и настраиваемых атрибутов сериализации JSON.
  • Группа инфраструктуры гипотетически хочет добавить атрибуты сериализации XML и некоторые настраиваемые атрибуты из сторонней реализации базы данных.
  • Команда Public API гипотетически хочет добавить атрибуты WCF.

Я не хочу давать каждой команде карт-бланш на добавление своих атрибутов в мою модель предметной области и особенно не хочу, чтобы они добавляли все свои "слои конкретные" зависимости от сборки моей модели.

И этот случай усложняется тем, что я использую сторонние «модели домена» в своих собственных (в данном случае с помощью Google LibPhoneNumber для обработки номера телефона).

В идеале каждый из них должен был бы создать свой собственный класс-оболочку, например:

using MyApplication.Domain;
namespace MyApplication.UI.DomainWrappers
{
    public class UISpeaker
    {
         private Speaker _speaker;
         public class UISpeaker(Speaker speaker = null)
         {
             _speaker = speaker ?? new Speaker();
         }

         [Required]
         public virtual string Name {
            get{ return _speaker.Name; }
            set{ _speaker.Name = value; }
         }

         [Required]
         public virtual PhoneNumber PhoneNumber {
            get{ return _speaker.PhoneNumber ; }
            set{ _speaker.PhoneNumber = value; }
         }

         //Conversion operators
         public static implicit operator UISpeaker(Speaker s)
         {
           return new UISpeaker(s);
         }

         public static implicit operator Speaker(UISpeaker s)
         {
           return s._speaker;
         }             
    }
}

Вопрос

Написание и поддержка класса UISpeaker — это боль и скучный шаблонный код.

Есть ли лучший способ добавить атрибуты, которые каждая команда хочет добавить, не позволяя им напрямую редактировать модель предметной области? Или есть какие-то инструменты, которые могут помочь сгенерировать эти классы-оболочки (я думал, что, возможно, инструмент ткачества, такой как Fody или шаблоны T4, но я я недостаточно знаком ни с тем, ни с другим, чтобы знать, могут ли они помочь в этом случае использования).

Исследовательская работа

Я просмотрел Stackoverflow и нашел несколько похожих вопросов, но ни один из них не отвечал всем требованиям, которые я ищу:


person Philip Pittle    schedule 15.01.2016    source источник
comment
В некоторых слоях вы можете использовать классы метаданных и связать их с основными классами, используя AssociatedMetadataTypeTypeDescriptionProvider. В некоторых слоях можно использовать классы-обертки/сопоставления и упростить задачи с помощью библиотек типа AutoMapper. Также во всех слоях вы можете упростить задания, используя некоторые стратегии генерации кода с использованием шаблонов t4.   -  person Reza Aghaei    schedule 15.01.2016
comment
@RezaAghaei - AssociatedMetadataTypeTypeDescriptionProviderвыглядит интересно, судя по краткой странице MSDN. Но знаете ли вы какой-нибудь пример кода, который позволит мне вводить дополнительные метаданные в класс, а затем использовать эти метаданные для стороннего кода?   -  person Philip Pittle    schedule 15.01.2016
comment
Вы можете найти этот пост полезным.   -  person Reza Aghaei    schedule 15.01.2016


Ответы (1)


Вы можете использовать эти параметры, чтобы упростить работу:

  • Классы метаданных
  • Преобразователи объектов в объекты
  • Генерация кода

Классы метаданных

Вы можете создавать классы метаданных и добавлять атрибуты, такие как аннотации данных и атрибуты проверки, к этим классам метаданных, а затем связывать эти классы метаданных с классами вашего основного домена, используя AssociatedMetadataTypeTypeDescriptionProvider. Такие классы метаданных являются только контейнерами атрибутов и с помощью механизмов дескрипторов типов добавляют атрибуты к вашим основным классам.

Например, вы можете таким образом зарегистрировать класс метаданных для своей модели и позволить всем инфраструктурам, которые приносят пользу TypeDescriptor, видеть ваши атрибуты метаданных для вашей модели:

var provider = new AssociatedMetadataTypeTypeDescriptionProvider(typeof(Model), 
                                                                 typeof(ModelMetadata));
TypeDescriptor.AddProvider(provider, typeof(Model));

Сопоставление объектов

Вы можете иметь модели просмотра, бизнес-модели и модели предметной области в разных слоях и использовать для их украшения атрибуты, которые вам нужны для каждого слоя, а затем использовать объект для сопоставления объектов, например AutoMapper упрощает задачу сопоставления этих классов друг с другом.

AutoMapper — это средство сопоставления объектов. Отображение объект-объект работает путем преобразования входного объекта одного типа в выходной объект другого типа. Что делает AutoMapper интересным, так это то, что он предоставляет некоторые интересные соглашения, позволяющие избавиться от грязной работы по выяснению того, как сопоставить тип A с типом B. Пока тип B следует установленному соглашению AutoMapper, для сопоставления двух типов требуется почти нулевая конфигурация.

Генерация кода

Вы можете упростить создание классов метаданных или классов моделей просмотра с помощью некоторых инструментов генерации кода. Например, вы можете создать классы-оболочки, просто используя механизм генерации кода, такой как T4 Templates.

В Visual Studio текстовый шаблон T4 представляет собой смесь текстовых блоков и управляющей логики, которая может создавать текстовый файл. Логика управления записывается в виде фрагментов программного кода на Visual C# или Visual Basic. Сгенерированный файл может быть текстом любого типа, например веб-страницей, файлом ресурсов или исходным кодом программы на любом языке.

person Reza Aghaei    schedule 15.01.2016
comment
при описании классов метаданных вы сказали, что все инфраструктуры, которые приносят пользу TypeDescriptor, видят ваши метаданные. Что ты имеешь в виду? Будет ли Json.Net, например, собирать дополнительные атрибуты сериализации, которые были добавлены таким образом? Скажи JsonIgnore? Или для того, чтобы это работало, вам нужно быть тем, кто проводит проверку типа? - person Philip Pittle; 19.01.2016
comment
Я не знаю, есть ли встроенная поддержка TypeDescriptor при использовании Json Serializer, но даже если встроенной поддержки нет, вы можете просто создать собственный распознаватель контрактов, производный от DefaultContractResolver, и сделать это самостоятельно. Также разрешите всем инфраструктурам, использующим TypeDescriptor, видеть ваши метаданные, это означает, что те части вашей инфраструктуры, которые поддерживают использование механизмов TypeDescriptor, например многие части ASP.NET MVC или все части Windows Forms, используют их. Также вы можете самостоятельно добавить поддержку TypeDescriptor в инфраструктуры. - person Reza Aghaei; 19.01.2016