聚合的实现二

DDD 学习笔记 | 极客时间 | 手把手教你落地 DDD

🏆 原文:16|聚合的实现(中):怎样实现不变规则?

业务规则必须在领域层实现。

当聚合根已经拥有了实现业务规则所需要的数据,首选直接在聚合根里实现业务,而不是在应用服务里实现。
如果业务较为复杂,可以考虑将业务规则封装到领域服务里。

聚合根中的业务规则,一般需要在聚合根内部完成。例如:某一个单独的技能(Skill)或工作经验(WorkExperience)对象自身是无法校验的,必须从员工聚合整体上考虑。

Assembler(装配器)

可以转换领域对象与应用层的 DTO

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
package chapter15.unjuanable.application.orgmng.empservice;
// imports...

@Component
public class EmpAssembler {
    EmpHandler handler; // Emp的领域服务
    OrgValidator orgValidator;

    @Autowired
    public EmpAssembler(EmpHandler handler, OrgValidator orgValidator) {
        this.handler = handler;
        this.orgValidator = orgValidator;
    }

    // 由 DTO 生成领域对象
    Emp fromCreateRequest(CreateEmpRequest request, User user) {
        //校验参数
        validateCreateRequest(request);
        
        // 生成员工号
        String empNum = handler.generateNum();

        Emp result = new Emp(request.getTenantId(), user.getId());
        result.setNum(empNum)
                .setIdNum(request.getIdNum())
                .setDob(request.getDob())
                .setOrgId(request.getOrgId())
                .setGender(Gender.ofCode(request.getGenderCode()));

        request.getSkills().forEach(s -> result.addSkill(
                s.getSkillTypeId()
                , SkillLevel.ofCode(s.getLevelCode())
                , s.getDuration()
                , user.getId()));

        request.getExperiences().forEach(e -> result.addExperience(
                e.getStartDate()
                , e.getEndDate()
                , e.getCompany()
                , user.getId()));

        return result;
    }

    void validateCreateRequest(CreateEmpRequest request) {
        //业务规则:组织应该有效
        orgValidator.orgShouldValid(
                request.getTenantId(), request.getOrgId());
    }

    // 将领域对象转换成 DTO
    EmpResponse toResponse(Emp emp) {
      // ...
    } 
}

Builder 与 Assembler 比较

  • Builder 是工厂模式的一种实现,工厂位于领域层,入口参数可以是基本类型、领域对象或者在领域层定义的 DTO,但不能是在应用层定义的 DTO
    • 优点:对领域逻辑的封装更彻底一些
    • 缺点:要写更多的代码和数据转换逻辑
  • Assembler 位于应用层,入口参数可以是应用层定义的 DTO
    • 优点:代码比较简洁
    • 缺点:有时领域逻辑可能稍有泻漏

Assembler 的命名只是一种常见的习惯,目的是和领域层的工厂相区别。

像 assembler 这样对 service 起辅助作用的类,一般统称为 Helper

Repository(仓库) 和传统的 DAO(数据访问对象)的区别

虽然都用来访问数据库,但有一个重要的区别:DAO 是针对单个表的,而 Repository 是针对整个聚合的。

comments powered by Disqus