【问题标题】:Immutability in aggregate child entities聚合子实体的不变性
【发布时间】:2019-10-31 18:18:30
【问题描述】:

我已经开始使用领域驱动设计原则,但目前遇到了一个特定问题。

我有一个具有多层嵌套子实体的聚合,如下所示:

public class Aggregate: Entity<AggregateId>, IAggregateRoot {
    private readonly List<ChildOne> childOnes;
}

public class ChildOne: Entity<ChildOneId> {
    public string ChildOneValue1;
    public string ChildOneValue2;
    public string ChildOneValue3;
    private readonly List<ChildTwo> childTwos;
}

public class ChildTwo: Entity<ChildTwoId> {
    public string ChildTwoValue1;
    public string ChildTwoValue2;
    public string ChildTwoValue3;
}

在域服务中,我需要访问 ChildOneChildTwo 的所有值,包括它们的 ID。

public interface IDomainService {
    public IEnumerable<INotification> Analyze(Aggregate aggregate);
}

但是,我不能按原样返回实体,因为这会违反不变性原则。

这让我想到我的域模型可能不是最优的,但我看不出这有什么不同,因为没有相应的父级嵌套实体永远不会存在。

另一种方法是使用一个值对象来保存给定实体的所有值,然后返回这个值对象而不是实体。但随后需要执行深度映射,因为域服务需要访问所有嵌套实体的值。

关于如何解决这个问题有什么建议吗?

【问题讨论】:

    标签: nested entity domain-driven-design


    【解决方案1】:

    关于如何解决这个问题有什么建议吗?

    有几种可能性。

    一种是反转您提议的 API - 设计域服务以接受不可变值作为参数,然后将该服务的实例传递给聚合,而不是相反。

    public interface IDomainService {
        public IEnumerable<INotification> Analyze(IEnumerable<DomainValue> values);
    }
    

    如果你回想一下《四人帮》书中的Visitor 模式,那是一个非常相似的想法。

    在某些情况下,内存状态机可能是无状态域服务的合理替代方案。在这种情况下,API 可能看起来像

    public interface IDomainService {
        public void OnAnalyze(DomainValue value);
        public IEnumerable<INotification> Notifications();
    }
    

    另一种可能性是将聚合设计为集合

    public class Aggregate {
        // ...
    
        public IEnumerable<DomainValue> YourCleverNameHere();
    }
    

    对于不更改聚合状态的用例,只需将聚合排除在外,直接使用当前状态的不可变内存表示即可。

    【讨论】:

    • 问题在于,为了执行分析,域服务需要同时来自ChildOne 集合的所有值以及来自嵌套ChildTwo 实体的值。由于嵌套结构对域服务很重要,我不能将所有值作为列表传递。据我所知,唯一的解决方案是将所有子实体深度映射到值对象的层次结构中,并将其传递给域服务。
    【解决方案2】:

    但是,我不能按原样返回实体,因为这会违反不变性原则。

    实体不需要是不可变的。在领域驱动设计的战术设计中,只有值对象是不可变的。

    就实体是聚合而言,您应该能够仅使用聚合根从存储库中获取/保存聚合。您应该无法获取/保存不是聚合根的实体。

    【讨论】:

    • 我知道子实体不必是不可变的。我的意思是,如果我返回一个子实体,我实际上将返回一个可变对象,该对象可以在其聚合根之外进行更改。但是,一种解决方案可能是将所有 ChildOne 及其嵌套的子实体映射到不可变值对象的层次结构中并返回它们。但我不确定这是最佳解决方案。
    • 当你返回子实体时,聚合仍然可以持有对它的引用。所以聚合可以知道对象是否发生了变异。实体可以通过在事务结束时对聚合根的引用来持久化
    • 你说得对——我没有这样想。但是,我还没有看到任何地方提到过这种技术,所以即使这可行,我想知道这是否是最佳实践?非常感谢您的帮助,但由于我是域驱动设计的新手,因此我努力遵循最佳实践:)
    • 如何对聚合进行版本控制?这很重要
    猜你喜欢
    • 1970-01-01
    • 2011-11-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多