Я адаптував дизайн, орієнтований на домен, вже близько 8 років, і навіть після всіх цих років є ще одне, що мене клопоче. Це перевірка наявності унікального запису у сховищі даних щодо об’єкта домену.
У вересні 2013 року Мартін Фаулер згадав про принцип TellDon'tAsk , який, якщо можливо, слід застосувати до всіх об'єктів домену, які потім повинні повернути повідомлення про те, як пішла операція (в об'єктно-орієнтованому дизайні це здебільшого робиться за винятками, коли операція була невдалою).
Мої проекти зазвичай поділяються на багато частин, де дві з них - «Домен» (містить правила бізнесу та нічого іншого; домен - повністю наполегливий) і Сервіс. Послуги, що знають про рівень сховища, який використовується для даних 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();
}
}
Це призводить до того, що у вас є служби, що визначають правила домену, а не сам доменний рівень, і ви маєте правила, розповсюджені по декількох розділах коду.
Я згадав про принцип TellDon'tAsk, оскільки, як ви добре бачите, сервіс пропонує дію (вона зберігає Product
або викидає виняток), але всередині методу ви керуєте об'єктами за допомогою процедурного підходу.
Очевидним рішенням є створення Domain.ProductCollection
класу Add(Domain.Product)
методом викидання ProductWithSKUAlreadyExistsException
, але це не вистачає продуктивності, тому що вам потрібно буде отримати всі Продукти зі сховища даних для того, щоб дізнатися в коді, чи продукт вже має ту саму SKU як Продукт, який ви намагаєтеся додати.
Як ви, хлопці, вирішуєте це конкретне питання? Це насправді не є проблемою, я вже мав службовий рівень представляти певні правила домену протягом багатьох років. Сервісний рівень зазвичай також обслуговує більш складні операції над доменом, мені просто цікаво, чи натрапили ви на краще, більш централізоване рішення протягом своєї кар’єри.