管理实体的历史对象

时间:2011-07-19 03:52:32

标签: design-patterns domain-driven-design aggregate

我们正在将现有系统转换为DDD,并且正在努力围绕一些概念。

我们有一个名为Animal的聚合根,它具有StatusSource等属性。目前,数据库有两个名为StatusHistorySourceHistory的表,它们在状态发生变化时存储有关动物的信息。这些表有时会删除记录,从Animal获取AnimalRepository时很少需要检索。

所以最重要的问题是它们属于哪里?以下是我们的一些想法:

  • 将它们作为动物聚合的一部分作为不同的实体对象。并且有相应的方法允许它们更新,例如:Animal.UpdateStatus(newStatus),它将使用new StatusHistory(this)对象添加到集合中。但是如上所述,当获取存储库的现有动物时,很少需要这些,因此我们不希望存储库加载它们。我们目前没有使用ORM,而是使用存储库中的表数据网关手动映射。

  • 使每个历史实体成为聚合根。我们不是这个的粉丝,因为我们觉得我们并没有真正对域进行建模而只是向Active Record Pattern漂移。另外,为动物更新动物的任务必须在动物实体之外。

  • 我们可以尝试将这些历史记录组合成另一个名为AnimalHistory的聚合根,其目的是维护动物的历史。但同样,它将把关于将历史存储到动物以外的其他东西的逻辑。可能像AnimalProcessingService这样的服务,感觉我们可能会走向贫血的设计。

我希望还有另一种方案可以为我们提供更清洁的设计。

2 个答案:

答案 0 :(得分:2)

这是Martin Fowler最近发表的一篇有趣的文章,可能会解决一些关于不需要检索的问题,称为Command Query Responsibility Segregation。基本概念是您可以使用不同的“模型”进行“查询”(阅读)而不是“命令”(保存)。

http://martinfowler.com/bliki/CQRS.html

仅仅因为你正在做DDD并不意味着所有东西都需要包含在适当的域对象中。设计您的域名与设计服务和事件等同样重要。我的观点是,通过关注“域”所需要的内容以及提供这些要求的解决方案,让您的域名更加自然。 DDD没有严格的方法论,它更多的是透视选择而不是正式的设计模式。因此,将历史对象作为实体根,如果它们仅用于保存则不一定是坏的。让您的“命令”相关服务构成正确的逻辑流程,以保存动物和历史。

我还想指出像Animal.UpdateStatus(newStatus)这样的东西是非常活跃的记录,你似乎想要避免它。

答案 1 :(得分:0)

如果您使用的是Ejb3,那么您可以为不同的听众编写自定义实现,例如 @PostInsert @PostUpdate 等事件。

在这些监听器中提供您的实现。例如,假设Animal中有更新,那么将调用PostUpdate侦听器,它将更新历史记录。

这种方法有一个很大的优点,因为一切都发生在一个事务中,一旦你编写了公共代码,你就不必费心去更新历史表了。

第二种方法将使用REFLECTION + INTERCEPTORS