【问题标题】:How to access entity within aggregate border如何访问聚合边界内的实体
【发布时间】:2017-10-15 13:16:52
【问题描述】:

我目前正在尝试在 DDD 中编写一个允许创建、更新和删除实体的应用程序。对实体的变更必须得到另一个人的批准。应用程序还必须跟踪对实体所做的更改。简化的域模型如下所示:

应用程序有一个包含ChangeSetEnityEntityHistory 的有界上下文,其中ChangeSet 是聚合根。我以这种方式设计了聚合,因为如果没有ChangeSet,则不应更改Entity,此外,ChangeSet 应与已编辑的实体一起保存在一个事务中。为此,我设计了一个单一的聚合体。

在创建新实体时设计工作非常好:

private void CreateChangeSet()
{
    var repository = new ChangeSetRepository();
    var entities = new List<Entity>
    {
        new Entity(Guid.NewGuid(), "Test1", new TagStatus(1, EntityState.Pending));
    };
    var changeSet = new ChangeSet("a user", "Added a new entity", DateTime.Now, ApprovalState.Submitted, entities);
    repository.Insert(changeSet);   
}

但是,当我尝试编辑实体时,我的设计中出现了问题:

private void EditEnity()
{
    var repository = new ChangeSetRepository();
    var entity = repository.GetEntityByName("Test1");    
    entity.AssignName("a new name");
    var entities = new List<Entity>{entity};

    var cs = new ChangeSet("a user", "Added a new entity", DateTime.Now, ApprovalState.Submitted, entities);
    repository.Insert(cs);
}

据我所知,存储库应该只返回聚合,这意味着为了更改Entity,我必须首先搜索一个没有意义的ChangeSet。即使您仅作为聚合根执行更改,返回聚合的子实体也是一种不好的做法吗?

我在互联网上搜索了一个答案,许多人指出这种查询可以指出聚合的错误设计。这让我再次思考,如果我需要两个聚合而不是一个聚合,一个用于ChangeSet,一个包含EntityEntityHistory。我应该使用两个聚合而不是一个吗?如果是这样,我怎样才能在单笔交易中做到这一点?

两个聚合的进一步指示是用户界面要求,例如“用户希望查看实体的更改历史记录”或“显示视图中的所有实体”。一方面这表明两个聚合,另一方面我觉得ChangeSetEntities 应该真正属于一起。

总结一下我的问题:

  • 我应该在设计中使用一种还是两种聚合?
  • 如果是一个聚合:即使您仅通过聚合根执行更改,返回聚合的子实体也是一种不好的做法吗?
  • 如果有两个聚合:如何在一个事务中保存ChangeSet 和关联的Entities

【问题讨论】:

  • 你为什么不看看事件溯源,事件将成为你的历史,你不需要做任何额外的事情?
  • 我会听从 Alessandro Santini 的建议,尝试一下 event sourcing。我感觉变更集不适合模型,事件溯源可以解决这个问题。
  • 这里的问题是版本控制问题更具技术性和通用性。因此,将其建模为领域模型的一部分可能是矫枉过正。您还可以考虑数据库版本控制模型,例如 RavenDb 有一个版本控制包,可以保留一定数量的文档版本。这也是一种有效的方法。

标签: c# domain-driven-design aggregate


【解决方案1】:

TL;DR:

  • 您应该使用一个实体。
  • 是的,这是不好的做法,因为行为应该由聚合公开;此外,重构Entity 需要Entity 知道如何查询ChangeSet;除非您在服务级别进行编排,否则这不是很好的设计。
  • 您不应该这样做,因为聚合根代表,恕我直言,事务边界

其他想法

如果我理解正确,您正在尝试做 Event Sourcing 自然地做的事情,并增加了批准工作流程。 Event Store 中的Events 与您使用ChangeSet 定义的大致相同。

如果这是正确的,您可以通过以下方式在 ES 中优雅地建模:

  • 调用 Edit Entity API,该 API 将 Entity 的大部分更改作为输入
  • API:
    • 从 API 输入构建ChangeEntityCommand(命令可能验证失败);
    • 检索Entity
    • Entity 聚合中调用相应的Handler,然后发出ChangeQueuedForApprovalEvent
    • 在 EventStore 中提交实体
  • EventHandler 将拦截上述事件并负责更新审批视图。

当批准者开绿灯时,类似的流程将发出一个ChangeApprovedEvent,其中包含前一个事件的相同数据。这个事件是真正改变Entity的事件。

最后,我不认为 ChangeSet 模型真的适合 DDD,因为它无法捕捉到变更的意图。

希望这对您的项目有所帮助并祝您好运。

【讨论】:

    猜你喜欢
    • 2014-08-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-29
    • 1970-01-01
    相关资源
    最近更新 更多