Какой правильный способ доступа к справочным данным в этом сценарии моделирования данных домена?

Начальное предупреждение: Длинный пост и я, возможно, все равно ошиблись:

Учитывая следующий класс, который является началом агрегата клиентов:

public class Customer : KeyedObject
{

   public Customer(int customerId)
   {
      _customerRepository.Load(this);
   }

   private ICustomerRepository _customerRepository = IoC.Resolve(..);  
   private ICustomerTypeRepository = _customerTypeRepository = IoC.Resolve(..);

   public virtual string CustomerName {get;set;}
   public virtual int CustomerTypeId [get;set;}

   public virtual string CustomerType
   {
      get
      {
         return _customerTypeRepository.Get(CustomerTypeId);
      }
   }

}

И CustomerType представлен объектом значения:

public class CustomerType : ValueObject
{
   public virtual int CustomerTypeId {get;set;}
   public virtual string Description {get;set;}
}

Это все хорошо, когда у меня есть объект customer с CustomerTypeId. Однако, когда я хочу заполнить DropDownList в моем MVC View, я пытаюсь понять, как правильно получить список значений CustomerType из ICustomerTypeRepostory.

ICustomerTypeRepository очень просто:

public interface ICustomerTypeRepository
{
   public CustomerType Get(int customerTypeId);
   public IEnumerable<CustomerType> GetList();
}

По сути, я хочу иметь возможность звонить ICustomerTypeRepository правильно из моего контроллера, однако я подумал, что было бы лучше отделить слой DAL (хранилище) от контроллера. Теперь я просто усложняю вещи?

Вот как сейчас работает мой контроллер:

public class CustomerController : ControllerBase
{ 

    private ICustomerTypeRepository _customerTypeRepository = IoC.Resolve(..);

    public ActionResult Index()
    {
       Customer customer = new Customer(customerId); 
       IEnumerable<CustomerType> customerTypeList = 
          _customerTypeRepository.GetList();

       CustomerFormModel model = new CustomerFormModel(customer);
       model.AddCustomerTypes(customerTypeList );
    }
}

Это кажется мне неправильным, поскольку у меня есть репозитории в контроллере и в клиенте. Мне кажется логичным, что для CustomerType должен быть разделен слой доступа. Т.е. CustomerType.GetList():

public class CustomerType : ValueObject
{
   // ... Previous Code

   private static ICustomerTypeRepository _customerTypeRepository = IoC.Resolve(..);

   public static IEnumerable<CustomerType> GetList()
   {
      _customerTypeRepository.GetList();
   }
}

Итак, вот каким образом я должен разоблачать CustomerType объекты через ICustomerTypeRepository к CustomerController?

2 ответа

Решение

Я думаю, что здесь есть несколько вещей для рассмотрения.

Прежде всего, если вы действительно заинтересованы в моделировании своего домена, вам нужно любой ценой попытаться защитить сами сущности домена от сквозных проблем, таких как проверка, контейнеры IoC и постоянство, несмотря на шаблон Active Record.

Это значит, что Customer вероятно, не должно иметь никаких ссылок на хранилище, даже если вы используете интерфейсы и локатор служб. Он должен быть спроектирован так, чтобы отражать атрибуты - или то, что составляет - "клиента" с точки зрения вашего целевого клиента / пользователя.

Помимо моделирования предметной области, я немного обеспокоен использованием вашего IoC сервисный локатор в инициализаторе переменной. Вы упускаете любую возможность ловить исключения и исключения, создаваемые конструкторами, как известно, трудно отладить (эти инициализаторы выполняются перед любым кодом в вашем первом нестатическом конструкторе).

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

Вместо того, чтобы иметь Customer объект заполняется данными с помощью вызова _customerRepository.Load(this) в конструкторе приложение будет чаще использовать хранилище для получения сущности, поэтому оно возвращается из полностью заполненного хранилища, включая CustomerType имущество. В этом случае кажется, что это может произойти в CustomerController,

Вы указываете, что вы хотите иметь отдельный DAL от уровня, в котором CustomerController находится, и у вас это есть - вот где вступает использование интерфейса репозитория. Вы внедряете некоторую реализацию этого интерфейса (или в этом случае получаете реализацию из реализации IoC), но фактическая реализация этого репозитория может существовать в отдельном слое (это может быть даже другая сборка). Это шаблон, известный как отдельный интерфейс.

Лично я бы реорганизовал CustomerController, чтобы он выглядел так:

public class CustomerController : ControllerBase
{ 
     private ICustomerTypeRepository _customerTypeRepository;
     private ICustomerRepository _customerRepository;

     public CustomerController(ICustomerRepository customerRepository,
        ICustomerTypeRepository customerTypeRepository)
     {
        _customerRepository = customerRepository;
        _customerTypeRepository = customerTypeRepository;
     }

     public ActionResult Index()
     {
         Customer customer 
             = _customerRepository.GetCustomerWithId(customerId); 
             // from where does customerId come?

         IEnumerable<CustomerType> customerTypeList 
             = _customerTypeRepository.GetTypes();

        . . .

     }
}

... и я бы взял все ссылки на репозитории из Customer и любой другой класс сущностей домена.

Как насчет изменения модели домена вашего клиента, чтобы включить свойство для CustomerTypes? Это также устранит необходимость попадания в хранилище каждый раз, когда вызывается CustomerType.

public class Customer : KeyedObject
{

   public Customer(int customerId)
   {
      _customerRepository.Load(this);

      ICustomerTypeRepository _customerTypeRepository = IoC.Resolve(..);
      _customerTypes = _customerTypeRepository.GetList();
   }

   private ICustomerRepository _customerRepository = IoC.Resolve(..);  

   public virtual string CustomerName {get;set;}
   public virtual int CustomerTypeId {get;set;}

   public virtual string CustomerType
   {
      get
      {
         return _customerTypes.Find(CustomerTypeId);
      }
   }

   private IEnumerable<CustomerType> _customerTypes;
   public virtual IEnumerable<CustomerType> CustomerTypes
   {
      get
      {
          return _customerTypes
      }
   }
}
Другие вопросы по тегам