Какой правильный способ доступа к справочным данным в этом сценарии моделирования данных домена?
Начальное предупреждение: Длинный пост и я, возможно, все равно ошиблись:
Учитывая следующий класс, который является началом агрегата клиентов:
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
}
}
}