DDD之六边形架构(Hexagonal Architecture)

领域驱动设计系列文章,点击上方合集↑

六边形架构(Hexagonal Architecture),也被称为端口和适配器架构(Ports and Adapters Architecture),是一种软件架构模式,用于构建可测试、可维护和灵活的应用程序。

1. 简介

六边形架构的设计思想源于Alistair Cockburn在2005年提出的"六边形关系图"理论。在这个理论中,软件系统被视为一个六边形,其中有三组组件构成:核心业务逻辑(Domain),输入和输出端口(Ports)以及适配器(Adapters)。这些组件通过一系列接口进行交互,实现内部的业务逻辑,并通过端口和适配器与外部系统进行交互。

  • 上图来自《实现领域驱动》这本书

2. 主要组件和原则

  1. 核心业务逻辑(Domain):核心业务逻辑是应用程序的核心部分,其中包含领域对象、实体、值对象、领域服务等。它独立于具体的技术实现,通过领域模型去描述和解决业务问题。

  2. 输入和输出端口(Ports):输入和输出端口定义了应用程序与外部世界的交互接口。输入端口用于接收来自外部系统的请求,输出端口用于向外部系统发送结果或状态更新。这些端口提供了抽象层,使核心业务逻辑与具体的外部依赖解耦。

  3. 适配器(Adapters):适配器是连接输入和输出端口与具体实现的桥梁。它们负责将外部世界的请求转换为适合核心业务逻辑处理的数据,并将结果适配为外部系统能够理解的形式。适配器可以是数据库、消息队列、外部服务库或任何其他与外部系统进行交互的方式。

在六边形架构中,核心业务逻辑位于六边形的中心,它不依赖于具体的外部实现。输入和输出端口围绕核心逻辑,提供与外部系统的交互接口。适配器将请求和响应合理地转换为适合核心逻辑的形式。这种架构模式使得我们可以更容易地替换、测试和调整外部依赖,因为核心逻辑与外部实现解耦。

3. 构建步骤

  1. 定义领域模型和核心业务逻辑:通过DDD的原则,识别并定义核心业务领域,并建立一个独立于技术实现的领域模型。

  2. 定义输入和输出端口:识别应用程序与外部系统的交互点,并设计相应的端口接口。这些端口应该抽象、可扩展和可测试。

  3. 实现适配器:根据具体的外部系统,实现适配器将外部请求转换为领域模型可以理解的格式,并将结果转换为外部系统可以处理的格式。

  4. 配置依赖注入(Dependency Injection):通过依赖注入,将适配器注入到核心业务逻辑中,确保它们能够无缝地协作。

  5. 编写测试:通过单元测试和整体测试,确保核心业务逻辑与外部系统的交互正常,并满足业务需求。

4. 具体代码演示

假设我们正在开发一个电商应用,需要实现商品管理的功能,包括添加商品、查询商品等操作。我们将使用Spring Cloud作为微服务框架,将核心层和外围层分离,使用适配器进行交互。

4.1 核心层的设计

首先,我们在核心层定义商品的领域模型和业务逻辑:

java 复制代码
public class Product {
    private String id;
    private String name;
    private double price;

    // getters and setters
}

public interface ProductService {
    Product addProduct(Product product);
    List<Product> getAllProducts();
    Product getProductById(String id);
}

4.2 外围层的设计

接下来,我们设计外围层,定义与外部系统的交互接口。

java 复制代码
@RestController
@RequestMapping("/api/products")
public class ProductController {

    private final ProductService productService;

    public ProductController(ProductService productService) {
        this.productService = productService;
    }

    @PostMapping
    public ResponseEntity<Product> addProduct(@RequestBody Product product) {
        Product addedProduct = productService.addProduct(product);
        return ResponseEntity.ok(addedProduct);
    }

    @GetMapping
    public ResponseEntity<List<Product>> getAllProducts() {
        List<Product> products = productService.getAllProducts();
        return ResponseEntity.ok(products);
    }

    @GetMapping("/{id}")
    public ResponseEntity<Product> getProductById(@PathVariable String id) {
        Product product = productService.getProductById(id);
        return ResponseEntity.ok(product);
    }
}

4.3 适配器的实现

我们使用Spring Cloud中的Feign客户端作为适配器,与其他微服务进行通信。

java 复制代码
@FeignClient(name = "product-service", url = "http://localhost:8080") 
// 这里假设商品服务的URL为http://localhost:8080
public interface ProductServiceFeignClient {

    @PostMapping("/api/products")
    Product addProduct(@RequestBody Product product);

    @GetMapping("/api/products")
    List<Product> getAllProducts();

    @GetMapping("/api/products/{id}")
    Product getProductById(@PathVariable String id);
}

4.4 依赖注入与配置

最后,我们通过依赖注入将适配器注入到核心业务逻辑中,使用Spring Cloud进行配置管理。

java 复制代码
@Configuration
public class ProductServiceConfiguration {

    @Bean
    public ProductService productService(ProductServiceFeignClient productServiceFeignClient) {
        return new ProductServiceAdapter(productServiceFeignClient);
    }
}

4.5 配置文件

在Spring Cloud中,还需要配置各个微服务的相关信息。

yaml 复制代码
spring:
  application:
    name: product-service

product-service:
  server:
    port: 8080

通过以上代码演示,我们将商品管理的核心业务逻辑与外部系统的交互逻辑进行了解耦,并支持了系统的扩展和替换。核心层的设计遵循领域驱动设计的原则,外围层通过适配器与外部系统交互,实现了六边形架构。

当我们需要对商品管理进行扩展时,可以通过修改核心层的代码来实现。当需要修改与外部系统的交互方式时,只需要修改适配器的实现即可,而不需要改动核心层的代码。

5. 结语

通过六边形架构,我们可以将关注点分离开来,提高系统的可测试性、可维护性和可扩展性。同时,该架构也使得领域驱动设计中的概念更加清晰,并促进了团队间的协作和集成。


关注微信公众号:"小虎哥的技术博客",让我们一起成为更优秀的程序员❤️!

相关推荐
老肖相当外语大佬16 天前
反DDD模式之“复用”
开源·实战·ddd·领域驱动设计
xin49718 天前
领域模型和数据模型还傻傻分不清? 如何实现领域模型
后端·领域驱动设计
老肖相当外语大佬19 天前
反DDD模式之关系型数据库
ddd·领域驱动设计·关系数据库·三范式
老肖相当外语大佬1 个月前
欢迎加入d3shop,一个DDD实战项目
开源·实战·ddd·领域驱动设计
老肖相当外语大佬1 个月前
图穷匕见-所有反DDD模式都是垃圾
ddd·领域驱动设计
老肖相当外语大佬1 个月前
主观与客观,破除DDD凭经验魔咒
java·ddd·领域驱动设计·dotnet
老肖相当外语大佬1 个月前
学习真DDD的最佳路径
ddd·领域驱动设计·软件设计
夜雨风云1 个月前
通过重构得到更深层的理解
重构·ddd·领域驱动设计
老肖相当外语大佬1 个月前
DDD是软件工程的第一性原理?
java·ddd·领域驱动设计·dotnet·软件设计
老肖相当外语大佬1 个月前
DDD建模后写代码的正确姿势(Java、dotnet双平台)
java·c#·ddd·领域驱动设计·dotnet