我已经适应领域驱动设计大约8年了,即使经过了这些年,仍然有一件事困扰着我。那就是针对域对象检查数据存储中的唯一记录。
2013年9月,马丁·福勒(Martin Fowler)提到了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作为您要添加的产品。
你们如何解决这个特定问题?就其本身而言,这并不是真正的问题,我已经让服务层代表某些域规则已有多年了。服务层通常还为更复杂的域操作提供服务,我只是想知道您在职业生涯中是否偶然发现了更好,更集中的解决方案。