【发布时间】:2022-01-03 12:40:35
【问题描述】:
在红皮书(实施领域驱动设计)中,Vernon Vaughn 展示了 Scrum 核心领域的聚合示例。不幸的是,代码示例只是片段。
第二次尝试将模型拆分为多个聚合,而不是使用单个大型聚合。因此,planBacklogItem 方法的方法契约从
public class Product ... {
...
public void planBacklogItem(
String aSummary, String aCategory,
BacklogItemType aType, StoryPoints aStoryPoints) {
...
}
...
}
到
public class Product ... {
...
public BacklogItem planBacklogItem(
String aSummary, String aCategory,
BacklogItemType aType, StoryPoints aStoryPoints) {
...
}
}
应用程序服务如下所示:
public class ProductBacklogItemService ... {
...
@Transactional
public void planProductBacklogItem(
String aTenantId, String aProductId,
String aSummary, String aCategory,
String aBacklogItemType, String aStoryPoints) {
Product product =
productRepository.productOfId(
new TenantId(aTenantId),
new ProductId(aProductId));
BacklogItem plannedBacklogItem =
product.planBacklogItem(
aSummary,
aCategory,
BacklogItemType.valueOf(aBacklogItemType),
StoryPoints.valueOf(aStoryPoints));
backlogItemRepository.add(plannedBacklogItem);
}
...
}
我理解聚合的概念和示例代码,但我想知道为什么 planBacklogItem 在多聚合版本中没有声明为静态。该方法不访问任何实例数据。使用单独的聚合来实现更好的并发访问。因此,我不明白为什么应用程序服务首先从存储库中提取一个完整的产品,而不需要它的任何数据。
由于 BacklogItem 使用 Id,因此无需从存储库中读取产品即可创建它。如果产品聚合有大量数据并且子聚合被频繁访问,则实施可能会导致性能问题。
我能想到的唯一解释是它应该确保产品的存在。但是为什么不使用 productRepository.existsProduct(productId) 之类的方法呢?
我正在使用 C#,但不了解 @Transactional 的所有魔力。沃恩没有说任何关于隔离级别的事情。读取产品和写入 BacklogItem 之间可能会出现竞争条件。 @Transactional 是否创建可序列化事务?我对此表示怀疑,因为并非所有存储都支持它。如果事务的隔离级别未序列化或产品在读取期间未锁定,则可以在写入 BackLogItem 之前将其删除。 (这可能不是 scrum 示例的商业案例,但它是一个普遍的问题)。
恐怕我错过了一些重要的事情。感谢您的帮助。
【问题讨论】:
标签: domain-driven-design aggregates