Я уже 8 лет адаптирую дизайн, ориентированный на предметную область, и даже после всех этих лет есть еще одна вещь, которая меня беспокоит. Это проверка уникальной записи в хранилище данных для объекта домена.
В сентябре 2013 года Мартин Фаулер упомянул принцип TellDontAsk , который, по возможности, должен применяться ко всем объектам домена, который затем должен возвращать сообщение о том, как прошла операция (в объектно-ориентированном проектировании это в основном делается с помощью исключений, когда операция была неудачной).
Мои проекты обычно делятся на множество частей, где две из них - это Домен (содержащий бизнес-правила и ничего более, домен полностью невежественен) и Службы. Сервисы, знающие о уровне хранилища, используемом для данных CRUD.
Поскольку уникальность атрибута, принадлежащего объекту, является доменом / бизнес-правилом, оно должно быть длинным для модуля домена, поэтому правило именно там, где и должно быть.
Чтобы иметь возможность проверить уникальность записи, вам нужно запросить текущий набор данных, обычно базу данных, чтобы выяснить, существует ли другая запись с, скажем, Name
уже существует.
Принимая во внимание, что уровень домена неосведомлен о постоянстве и не имеет представления о том, как извлечь данные, а только о том, как выполнять над ними операции, он не может действительно затрагивать сами репозитории.
Дизайн, который я тогда адаптировал, выглядит так:
class ProductRepository
{
// throws Repository.RecordNotFoundException
public Product GetBySKU(string sku);
}
class ProductCrudService
{
private ProductRepository pr;
public ProductCrudService(ProductRepository repository)
{
pr = repository;
}
public void SaveProduct(Domain.Product product)
{
try {
pr.GetBySKU(product.SKU);
throw Service.ProductWithSKUAlreadyExistsException("msg");
} catch (Repository.RecordNotFoundException e) {
// suppress/log exception
}
pr.MarkFresh(product);
pr.ProcessChanges();
}
}
Это приводит к тому, что службы определяют правила домена, а не сам уровень домена, а правила разбросаны по нескольким разделам кода.
Я упомянул принцип TellDontAsk, потому что, как вы можете ясно видеть, сервис предлагает действие (либо сохраняет, Product
либо выдает исключение), но внутри метода вы работаете с объектами, используя процедурный подход.
Очевидное решение состоит в том, чтобы создать Domain.ProductCollection
класс с Add(Domain.Product)
методом броска ProductWithSKUAlreadyExistsException
, но ему не хватает производительности, потому что вам нужно было бы получить все Продукты из хранилища данных, чтобы узнать в коде, имеет ли Продукт тот же самый SKU как продукт, который вы пытаетесь добавить.
Как вы, ребята, решаете эту конкретную проблему? На самом деле это не проблема, у меня есть уровень обслуживания, представляющий определенные правила домена в течение многих лет. Сервисный уровень обычно также обслуживает более сложные доменные операции, мне просто интересно, наткнулись ли вы на лучшее, более централизованное решение в течение своей карьеры.