Skip to content

Latest commit

 

History

History
72 lines (45 loc) · 3.67 KB

从事务脚本到领域驱动3.md

File metadata and controls

72 lines (45 loc) · 3.67 KB

从事务脚本到领域驱动(3)——六边形架构

不知道你是否与笔者一样长久以来一直有一些纠结的疑惑:

  • 如果把提供 Web api 的 Controller 看做一个 Facade,那么 Application Service 作为另一个 Facade 是否还有存在的必要?

  • 当某个业务操作需要向外部系统发消息时,是否可以直接依赖外部系统的 Web Service 接口?还是应该依赖更为抽象的接口?这个接口定义在哪一层?

  • Repository 是否和 DAO 是同一个东西的两个名字?为什么只有聚合根才能有 Repository?

我们可以借助六边形架构让系统结构更加清晰。六边形架构的思想是将输入和输出都放在设计的边缘部分,并且明确区分系统的内部外部。系统内部通过端口和适配器与系统外部交互。可以把系统内部想象为一个手机,端口可以是一个 type-c 接口,适配器可以是任意一个符合标准的电源适配器,或者任意一个符合标准的充电宝。手机的充电模块可以只针对 type-c 接口和相关标准进行设计。一个端口可以通过任意多个适配器与系统外部环境交互。

适配器分为主动适配器(Driving Adapters)被动适配器(Driven Dadpters)两种。

  • 主动适配器(Driving Adapters),也就是适配器会主动调用端口。典型的主动适配器是 Rest Web api 和 WebService api。Application Service 层非常适合作为被它们调用的端口。

  • 被动适配器(Driven Dadpters),是指适配器被系统内部调用。由于不允许系统内部直接依赖适配器,所以要使用依赖倒置,只允许系统内部依赖一些窄接口(作为被动适配器的端口),然后由被动适配器实现这些接口。典型的被动适配器包括向外部系统发消息的 WebService 客户端、消息队列,以及访问数据库的 DAO。

对于企业信息系统,数据库访问的代码量很大。为简单起见,数据库访问的端口就直接使用Mybatis plus的mapper接口,例如:

package com.bzb.atjob.app.auth.adaptor.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.bzb.atjob.app.auth.core.dept.model.Dept;

public interface DeptMapper extends BaseMapper<Dept> {}

端口的数据库访问适配器会由 Mybatis plus 自动生成,不需要我们自己写实现类。

尽量不使用 SQL 层面的关联查询,当一个聚合根需要查询另一个聚合根时,可依赖一个作为端口的窄接口:

package com.bzb.atjob.app.auth.adaptor.finder;

import com.bzb.atjob.app.auth.core.role.model.Role;

@FunctionalInterface
public interface FindRoleById {
  Role findRoleById(String roleId);
}

此端口可以有多个适配器:

  • 如果 Role 存在于外部系统,需要增加一个使用 WebService 客户端获取数据的实现类。

  • 如果 Role 存在于系统内部,为简单起见,直接让 RoleRepository 实现该端口:

    package com.bzb.atjob.app.auth.core.role.repository;
    
    @Service
    @RequiredArgsConstructor
    public class RoleRepository implements FindRoleById {
      @Override
      public Role findRoleById(String roleId) {
        return this.byId(roleId);
      }
    }

Repository

Repository 不是 DAO。Repository 是更为抽象一点的聚合根的“家”。可以这样想象一下,如果我们拥有一台无限内存,永不宕机的服务器——也就是不需要数据持久化,是否还需要Repository?

即使这样我们仍然需要Repository。

  • 我们需要 DeptRepository 告诉我们目前还有多少存活的 Dept 实例。

  • User 只保留了 deptId,它需要能够使用此Id得到 Dept 实例。