首页 > 解决方案 > DDD 中域对象集合的最佳实践

问题描述

我有以下课程:

public class DomainClass {
    private Integer value;
    private Integer total;
    private Integer month;
    public Double getPercent(){/*Business logic*/}
}

我想对没有重复代码的 DomainClass 对象列表执行相同的 getPercent 操作。我有两个想法来处理这个问题,但我不知道它们是否好。

想法 1 - 创建一个服务并迭代列表:

public class DomainObjectService{
....
public Double getPercent(List<DomainClass> list){
    double value, total;
    for(DomainClass d : list){
        value += d.getValue();
        total += d.getTotal();
    }
    // do percent operation
}

思路二——在数据库中查询操作,填充对象,调用业务方法

public class DomainObjectService{
....
public Double getPercent(){
    double value, total;
    .... query data with sql and set the above variables

    // do percent operation
    return new DomainBusiness(value, total).getPercentage();
}

我正在阅读,在 DDD 中,实体应该处理自己的逻辑,但在这种收集操作的情况下,应该如何处理它?

好吧,如果我的 DDD 基础知识是错误的。我想知道Java中DDD的好文章/书籍/示例。

标签: javadesign-patternsdomain-driven-design

解决方案


您如何管理您的实体?你使用任何类型的 ORM 吗?


我对这种操作的解决方案是构建一个管理对象集合的类。因此,例如:

public class DomainClasses{
   private final List<DomainClass> domainClasses;

   ....

   public Double getPercent(){
      // manage the percent operation   ...
      // ... on all the members the way ... 
      // ... your business is expected  ...
      // ... to do it on the collection

      return something;
   }

   // class initialization
}

通过这种方式,您可以重用每个类的 getPercent 代码,还可以实现它的特定版本以供集合使用。此外,该集合可以访问 DomainClass 的包私有 getter(如果有)来进行计算。通过这种方式,您只公开构建域对象所需的功能。

注意:如果您在没有任何 ORM 的情况下管理持久性,则此解决方案是可行的。或者,如果您想使用它,则需要额外的工作才能正确配置容器类。

一些链接:

https://www.mehdi-khalili.com/orm-anti-patterns-part-4-persistence-domain-model(我使用与 PM 分离的 DM)

https://softwareengineering.stackexchange.com/questions/371344/why-not-use-an-orm-with-ddd(我正在做的是将域对象转换为将被持久化的 DTO - 这有点为集合编写额外的代码,但一旦经过测试,它就可以正常工作,而且你得到的是一个不受 ORM 框架干扰的域)


提问后更新。我使用备忘录模式。

存储

My Domain Class 具有导出 Memento 对象中所有数据的功能。存储库需要一个域实例,请求 Memento,然后:

  • 我生成 SQL 插入/更新(来自 Spring 的带有事务管理的普通 SQL)
  • 您可以加载您的 JPA 实体并使用 Memento 信息进行更新(应小心,但如果您编写测试,一旦完成它将始终有效 - 因此,测试很重要;))

阅读

相反,从保存的数据构建域实例,我这样做:

  • 在实现存储库代码的持久层中,我扩展了我的 Memento(我们称之为PersistedMemento
  • 当我必须加载某些东西时,我构建了一个PersistedMemento并使用它来构建域类的实例
  • 我的域类具有允许从纪念品构建对象的功能。注意:这并不总是必要的,但在我的情况下,主构造函数有额外的检查,当对象从保存的对象重建时无法完成。无论如何,这简化了 Domain 类的重建。

为了保护域类不被域之外的世界使用:

  • 我的存储库需要一个现有的事务,因此它们不能直接在代码中的任何地方使用
  • Memento 类具有受保护的构造函数,因此它们只能在 Domain 包或 Repository 包中使用。PersistedMemento也隐藏在Repository 包中,因此无法创建任何实例。

笔记

当然不是完美的解决方案。域类有 2 个函数来支持非域需求。Memento 类也可以是子类,并且可以使用一个实例来构建域类(但是为什么呢?使用默认构造函数构建它要简单得多)。但是,除了这少量的污染,域保持非常干净,我可以真正专注于域需求,而不考虑如何管理持久性。


推荐阅读