微服务架构模型有很多种,例如洋葱架构、CQRS和六边形架构等。虽然这些架构模式提出的时代和背景不同,但其核心理念都是为了设计出“高内聚,低耦合”的微服务,轻松实现微服务的架构演进。DDD分层架构的出现,使微服务的架构边界变得越来越清晰,在微服务架构模型中,占有非常重要的位置。那DDD分层架构到底是什么样?DDD分层架构如何推动架构演进?我们该怎么转向DDD分层架构?本章将重点解决这些问题 。
1、什么是DDD分层架构
DDD分层架构其实也在不断发展和演进。早期的DDD分层架构是一种各层都依赖于基础层的传统四层架构,后来这种四层架构进一步优化,实现了各层对基础层的解耦和依赖倒置。
在最早的传统四层架构中,基础层被其他层依赖,位于最核心的位置,是其他各层依赖的核心。但实际上领域层才是软件的核心,所以这种依赖关系是有问题的。后来就采用了依赖倒置设计,各层服务通过仓储接口访问基础层,从而优化了传统的DDD四层架构,实现了DDD各层对基础层的解耦 。
我们本章要重点讲解的就是优化后的DDD分层架构。DDD分层架构中包含四层,从上到下依次是:用户接口层、应用层、领域层和基础层。
1.1 用户接口层
在很多描述DDD用户接口层的文章中,对用户接口层的解释通常都是这样的:“用户接口层负责向用户显示信息和解释用户指令,这里的用户可能是用户、程序、自动化测试和批处理脚本等。”但随着微服务架构的盛行,大多数应用都采用了前后端分离的设计模式。为了连接前端应用和后端微服务,于是又出现了API网关 。
结合洋葱模型和端口适配器架构模型,我在这里稍微拓展一下用户接口层的作用。在微服务面向不同前端应用时,同样的一段业务逻辑,可能由于渠道不同,而在前端展示的页面要素不同,因此要求后端微服务返回的数据结果会不同。例如在面向内部员工的PC端应用时,可能要求返回某些对象的全部属性的数据,而在面向外部客户的移动端应用时,可能只需要返回几个关键属性的数据就可以了。
为了避免暴露微服务的核心业务逻辑,防止数据外泄,不能将后端数据直接暴露给前端应用。为此,可以在用户接口层引入数据传输对象(DTO)和门面接口(Facade Interface),通过这些适配器来提供灵活的数据接口,满足不同前端应用的需求 。
1.2 应用层
应用层主要负责服务的组合和编排,实现业务逻辑。应用服务通过调用领域层的功能来处理用户请求,并将处理结果返回给用户。应用层的设计需要关注以下几个方面:
- 服务编排:根据业务需求,调用多个领域服务,实现业务功能。
- 事务管理:管理业务操作的事务,确保数据一致性。
- 安全控制:处理用户权限和身份验证,确保只有合法用户可以执行操作 。
应用层通常通过应用服务(Application Services)来实现。应用服务是应用层的核心组件,负责处理具体的业务操作。例如,订单服务的实现可能如下:
public class OrderApplicationService {private final OrderRepository orderRepository;private final PaymentService paymentService;public OrderApplicationService(OrderRepository orderRepository, PaymentService paymentService) {this.orderRepository = orderRepository;this.paymentService = paymentService;}public void placeOrder(OrderDTO orderDTO) {Order order = new Order(orderDTO);orderRepository.save(order);paymentService.processPayment(order.getPaymentDetails());}public OrderDTO getOrder(String orderId) {Order order = orderRepository.findById(orderId);return new OrderDTO(order);}
}
1.3 领域层
领域层是DDD分层架构的核心,负责处理系统的业务逻辑。领域层包含领域模型和领域服务,通过对领域对象和领域行为的建模,实现业务需求 。
领域层的组成
领域层通常包含以下组件:
- 实体(Entity):具有唯一标识符的对象,表示业务中的关键概念。
- 值对象(Value Object):无唯一标识符的对象,表示业务中的描述性概念。
- 聚合(Aggregate):一组相关对象的集合,以聚合根(Aggregate Root)为唯一入口。
- 领域服务(Domain Service):处理跨实体的业务逻辑。
- 仓储(Repository):负责实体的持久化和检索 。
领域层的职责
- 建模业务逻辑:通过实体、值对象和聚合等模型来表示业务逻辑。
- 执行业务操作:通过领域服务执行复杂的业务操作。
- 维护数据一致性:确保领域模型的状态在业务操作后保持一致 。
领域层的实现需要结合具体的业务需求进行设计。以下是一个简单的领域层实现示例:
public class Order {private OrderId id;private List<OrderItem> items;private OrderStatus status;public Order(OrderId id, List<OrderItem> items) {this.id = id;this.items = items;this.status = OrderStatus.CREATED;}public void addItem(OrderItem item) {items.add(item);}public void removeItem(OrderItem item) {items.remove(item);}public void place() {if (items.isEmpty()) {throw new IllegalStateException("Cannot place an order with no items.");}this.status = OrderStatus.PLACED;}// getters and other methods
}
1.4 基础层
基础层提供系统运行所需的技术支持和基础服务。基础层的设计需要考虑系统的性能、可靠性和可扩展性。
基础层的职责
- 数据持久化:负责将领域对象持久化到数据库中。
- 消息传递:处理系统内外部的消息传递,如事件总线、消息队列等。
- 第三方集成:与外部系统或服务集成,如支付网关、邮件服务等 。
基础层的实现通常涉及数据库访问、消息中间件、外部服务调用等。以下是一个基础设施层的简单实现示例:
public class JpaOrderRepository implements OrderRepository {private final EntityManager entityManager;public JpaOrderRepository(EntityManager entityManager) {this.entityManager = entityManager;}@Overridepublic void save(Order order) {entityManager.persist(order);}@Overridepublic Order findById(OrderId id) {return entityManager.find(Order.class, id);}
}
1.5 DDD分层架构的重要原则
DDD分层架构有一个重要的依赖原则:“每层只能与位于其下方的层发生耦合”。根据耦合的紧密程度,可以分为两种架构模式:严格分层架构和松散分层架构 。
严格分层架构
严格分层架构要求每一层只能依赖于其下层的接口。这种设计使得系统的各个部分可以独立演进,但也可能导致较多的层间调用和性能开销。
松散分层架构
松散分层架构允许上层直接访问底层的某些服务或资源,从而减少层间调用的开销。这种设计在某些性能敏感的场景中非常有效,但需要小心管理依赖关系,避免系统变得难以维护。
2、DDD分层架构如何推动架构演进
业务和技术不断变化,领域模型也会随之演进,从而影响微服务的功能和边界。通过DDD分层架构,我们可以实现领域模型和微服务的同步演进,推动微服务架构的演进 。
2.1 微服务架构的演进
在微服务架构的演进过程中,DDD分层架构提供了一种清晰的模型,通过明确各层的职责和边界,使得系统的扩展和维护更加便捷。各层之间通过接口进行通信,实现了服务的高内聚、低耦合。
2.2 微服务内服务的演进
在微服务内部,服务的演进可以通过领域模型的重构和优化来实现。通过引入新的领域对象和服务,改进现有的业务逻辑,确保系统能够灵活应对业务需求的变化。
3、三层架构如何演进到DDD分层架构
传统企业应用大多是单体架构,而单体架构多采用三层架构。三层架构可以部分解决程序内代码间调用复杂、代码职责不清的问题,但这种分层是逻辑概念,在物理上它仍然是集中式架构,所以并不适合分布式微服务架构 。
其实,DDD分层架构内的基本要素和三层架构类似,只不过在DDD分层架构中,这些要素被重新归类,划分到了不同的层,确定了层与层之间的交互规则和职责边界。在三层架构向DDD分层架构演进时,主要变化发生在业务逻辑层和数据访问层 。
业务逻辑层的变化
DDD分层架构对三层架构的业务逻辑层进行了更清晰的划分,改善了三层架构核心业务逻辑混乱、代码改动相互影响大的问题。DDD分层架构将三层架构业务逻辑层的业务逻辑拆分到了应用层和领域层,分别以应用服务和领域服务等形式存在。应用服务实现服务的组合和编排,领域服务完成核心领域逻辑,应用服务可以快速响应前端业务和流程的变化,而领域层则更加专注领域模型和实现领域逻辑 。
数据访问层的变化
数据访问层的变化主要发生在数据访问层和基础层之间。三层架构数据访问采用DAO方式,而DDD分层架构对数据库等基础资源访问时采用了仓储设计模式,领域层可以通过仓储接口访问基础资源的实现逻辑。这样,通过依赖倒置实现了各层对基础资源的解耦。原来三层架构的第三方工具包、驱动、Common、Utility、Config等通用的、公共的基础资源统一放到了基础层 。
另外,DDD分层架构在用户接口层引入了DTO和facade接口,可以给前端应用提供更灵活的数据和接口适配能力 。
4、 本章小结
DDD分层架构是微服务设计和开发的核心框架。通过用户接口层、应用层、领域层和基础层这些层次划分,可以明确微服务内各层的职能,划定各领域对象的边界,确定各领域对象的依赖和协作方式 。
传统企业架构向DDD分层架构的转变,可以通过重构业务逻辑层和数据访问层,采用领域驱动设计的原则和方法,逐步实现系统的演进和优化 。
================================
DDD分层架构详解
领域驱动设计(DDD,Domain-Driven Design)是一种设计方法论,旨在通过紧密结合业务领域和软件设计来解决复杂业务问题。DDD提倡从业务需求出发,建立清晰的领域模型,并根据该模型设计系统架构。在DDD的实现中,分层架构是关键的设计模式之一。本文将详细探讨DDD分层架构的各个方面,包括其基本概念、各层职责、实现策略及其在实际项目中的应用。
1. DDD分层架构的历史与演变
DDD分层架构最早的形式是一种各层都依赖于基础层的传统四层架构。在这种架构中,基础层位于核心位置,其他各层依赖于基础层。然而,随着软件架构的不断发展,这种依赖关系逐渐暴露出问题。为了优化架构设计,依赖倒置原则被引入,各层通过接口访问基础层,实现了对基础层的解耦。
1.1 分层架构的演变
分层架构的演变经历了多个阶段,主要包括三层架构、传统四层架构和优化后的四层架构。
-
三层架构:传统的三层架构包括表示层、业务逻辑层和数据访问层。这种架构可以部分解决代码职责不清的问题,但在物理上仍然是集中式架构,不适合分布式微服务架构。
-
传统四层架构:传统四层架构在三层架构的基础上增加了基础层,各层都依赖于基础层。然而,这种设计并没有充分考虑领域层的重要性,导致依赖关系不合理。
-
优化后的四层架构:优化后的四层架构通过引入依赖倒置原则,各层通过仓储接口访问基础层,实现了对基础层的解耦,明确了领域层的核心地位。
2. DDD分层架构的组成
DDD分层架构通常包括四层,从上到下依次是:用户接口层、应用层、领域层和基础层。每一层都有明确的职责和边界,彼此之间通过接口进行通信。
-
用户接口层(User Interface Layer):负责接收用户的输入和返回处理结果。
-
应用层(Application Layer):负责服务的组合和编排,实现业务逻辑。
-
领域层(Domain Layer):包含领域模型和领域逻辑,是系统的核心,负责实现核心业务逻辑。
-
基础层(Infrastructure Layer):提供通用的技术和基础服务,包括数据持久化、消息传递等。
2.1 用户接口层
用户接口层是系统与外部交互的部分,负责处理用户的输入并将处理结果返回给用户。用户接口层通过API网关连接前端应用和后端微服务。
2.1.1 用户接口层的职责
-
接收用户输入:通过API接收前端或其他系统的请求。
-
展示处理结果:将应用层处理的结果通过API返回给前端或其他系统。
-
输入验证:对用户输入的数据进行初步验证,确保数据格式和内容的合法性。
2.1.2 用户接口层的实现
用户接口层通常通过RESTful API或GraphQL等技术实现。例如:
@RestController
@RequestMapping("/orders")
public class OrderController {private final OrderApplicationService orderApplicationService;public OrderController(OrderApplicationService orderApplicationService) {this.orderApplicationService = orderApplicationService;}@PostMappingpublic ResponseEntity<Void> placeOrder(@RequestBody OrderDTO orderDTO) {orderApplicationService.placeOrder(orderDTO);return ResponseEntity.status(HttpStatus.CREATED).build();}@GetMapping("/{id}")public ResponseEntity<OrderDTO> getOrder(@PathVariable String id) {OrderDTO orderDTO = orderApplicationService.getOrder(id);return ResponseEntity.ok(orderDTO);}
}
2.2 应用层
应用层负责协调用户接口层和领域层,执行应用逻辑。应用层不包含领域逻辑,只负责调用领域层的功能,处理用户请求并返回结果。
2.2.1 应用层的职责
-
服务编排:根据业务需求,调用多个领域服务,实现业务功能。
-
事务管理:管理业务操作的事务,确保数据一致性。
-
安全控制:处理用户权限和身份验证,确保只有合法用户可以执行操作。
2.2.2 应用层的实现
应用层通常通过应用服务(Application Services)来实现。应用服务是应用层的核心组件,负责处理具体的业务操作。以下是一个典型的应用服务实现示例:
public class OrderApplicationService {private final OrderRepository orderRepository;private final PaymentService paymentService;public OrderApplicationService(OrderRepository orderRepository, PaymentService paymentService) {this.orderRepository = orderRepository;this.paymentService = paymentService;}public void placeOrder(OrderDTO orderDTO) {Order order = new Order(orderDTO);orderRepository.save(order);paymentService.processPayment(order.getPaymentDetails());}public OrderDTO getOrder(String orderId) {Order order = orderRepository.findById(orderId);return new OrderDTO(order);}
}
2.3 领域层
领域层是DDD分层架构的核心,负责处理系统的业务逻辑。领域层包含领域模型和领域服务,通过对领域对象和领域行为的建模,实现业务需求。
2.3.1 领域层的组成
领域层通常包含以下组件:
-
实体(Entity):具有唯一标识符的对象,表示业务中的关键概念。
-
值对象(Value Object):无唯一标识符的对象,表示业务中的描述性概念。
-
聚合(Aggregate):一组相关对象的集合,以聚合根(Aggregate Root)为唯一入口。
-
领域服务(Domain Service):处理跨实体的业务逻辑。
-
仓储(Repository):负责实体的持久化和检索。
2.3.2 领域层的职责
-
建模业务逻辑:通过实体、值对象和聚合等模型来表示业务逻辑。
-
执行业务操作:通过领域服务执行复杂的业务操作。
-
维护数据一致性:确保领域模型的状态在业务操作后保持一致。
2.3.3 领域层的实现
领域层的实现需要结合具体的业务需求进行设计。以下是一个简单的领域层实现示例:
public class Order {private OrderId id;private List<OrderItem> items;private OrderStatus status;public Order(OrderId id, List<OrderItem> items) {this.id = id;this.items = items;this.status = OrderStatus.CREATED;}public void addItem(OrderItem item) {items.add(item);}public void removeItem(OrderItem item) {items.remove(item);}public void place() {if (items.isEmpty()) {throw new IllegalStateException("Cannot place an order with no items.");}this.status = OrderStatus.PLACED;}// getters and other methods
}
2.4 基础层
基础层提供系统运行所需的技术支持和基础服务。基础层的设计需要考虑系统的性能、可靠性和可扩展性。
2.4.1 基础层的职责
-
数据持久化:负责将领域对象持久化到数据库中。
-
消息传递:处理系统内外部的消息传递,如事件总线、消息队列等。
-
第三方集成:与外部系统或服务集成,如支付网关、邮件服务等。
2.4.2 基础层的实现
基础层的实现通常涉及数据库访问、消息中间件、外部服务调用等。以下是一个基础设施层的简单实现示例:
public class JpaOrderRepository implements OrderRepository {private final EntityManager entityManager;public JpaOrderRepository(EntityManager entityManager) {this.entityManager = entityManager;}@Overridepublic void save(Order order) {entityManager.persist(order);}@Overridepublic Order findById(OrderId id) {return entityManager.find(Order.class, id);}
}
3. DDD分层架构的重要原则
DDD分层架构有一个重要的依赖原则:“每层只能与位于其下方的层发生耦合”。根据耦合的紧密程度,可以分为两种架构模式:严格分层架构和松散分层架构。
3.1 严格分层架构
严格分层架构要求每一层只能依赖于其下层的接口。这种设计使得系统的各个部分可以独
立演进,但也可能导致较多的层间调用和性能开销。
3.2 松散分层架构
松散分层架构允许上层直接访问底层的某些服务或资源,从而减少层间调用的开销。这种设计在某些性能敏感的场景中非常有效,但需要小心管理依赖关系,避免系统变得难以维护。
4. DDD分层架构的优势
DDD分层架构通过明确各层的职责和边界,实现了系统的高内聚、低耦合,具有以下优势:
-
提高系统可维护性:每一层都有明确的职责和边界,代码更加清晰,便于维护和扩展。
-
增强系统灵活性:通过分层设计,可以在不影响其他层的情况下,对某一层进行修改和优化。
-
促进团队协作:不同层次的职责划分明确,团队成员可以专注于各自负责的层次,提高开发效率。
5. DDD分层架构的实践
在实际项目中,DDD分层架构的应用需要结合具体的业务需求和技术环境进行调整。以下是一些实践建议:
5.1 选择合适的分层策略
根据系统的复杂度和业务需求,选择合适的分层策略。对于小型项目,可以采用简化的三层架构;对于大型项目,则可以采用完整的四层架构。
5.2 遵循依赖倒置原则
在DDD分层架构中,低层不应该依赖于高层,高层应该通过接口依赖于低层。这一原则通过依赖倒置(Dependency Inversion Principle, DIP)来实现,有助于降低层之间的耦合。
5.3 结合CQRS模式
命令查询责任分离(Command Query Responsibility Segregation, CQRS)是一种常见的设计模式,将写操作和读操作分离,以提高系统的性能和扩展性。在DDD分层架构中,可以结合CQRS模式,实现更加灵活的架构设计。
5.4 应用事件驱动架构
事件驱动架构(Event-Driven Architecture, EDA)是一种基于事件的系统设计模式,适用于处理复杂业务逻辑和高并发场景。在DDD分层架构中,可以通过领域事件和事件总线,实现各层之间的解耦和异步处理。
5.5 实现自动化测试
自动化测试是确保系统质量的重要手段。在DDD分层架构中,可以通过单元测试、集成测试和端到端测试等多种方式,对各层进行全面测试,确保系统的稳定性和可靠性。
6. 案例分析:电商系统的DDD分层架构
为了更好地理解DDD分层架构的应用,以下以一个电商系统为例,进行详细的案例分析。
6.1 需求分析
一个典型的电商系统需要处理以下业务需求:
-
用户管理:注册、登录、用户信息管理等。
-
商品管理:商品的增删改查、分类管理、库存管理等。
-
订单管理:订单创建、支付、配送、订单状态管理等。
-
促销管理:优惠券、折扣活动等。
6.2 系统架构设计
根据以上需求,可以设计一个基于DDD分层架构的电商系统。系统架构图如下:
+--------------------+
| 用户接口层 |
+--------------------+
| 应用层 |
+--------------------+
| 领域层 |
+--------------------+
| 基础设施层 |
+--------------------+
6.3 各层职责与实现
用户接口层:负责处理用户的注册、登录、浏览商品、下单等操作。可以使用前端框架(如React、Vue)实现。
应用层:负责协调用户接口层和领域层,处理用户请求并返回结果。包括用户服务、商品服务、订单服务等。
public class OrderApplicationService {private final OrderRepository orderRepository;private final PaymentService paymentService;public OrderApplicationService(OrderRepository orderRepository, PaymentService paymentService) {this.orderRepository = orderRepository;this.paymentService = paymentService;}public void placeOrder(OrderDTO orderDTO) {Order order = new Order(orderDTO);orderRepository.save(order);paymentService.processPayment(order.getPaymentDetails());}public OrderDTO getOrder(String orderId) {Order order = orderRepository.findById(orderId);return new OrderDTO(order);}
}
领域层:包含用户、商品、订单等领域模型及其业务逻辑。通过实体、值对象、聚合等模型表示业务逻辑。
public class Order {private OrderId id;private List<OrderItem> items;private OrderStatus status;public Order(OrderId id, List<OrderItem> items) {this.id = id;this.items = items;this.status = OrderStatus.CREATED;}public void addItem(OrderItem item) {items.add(item);}public void removeItem(OrderItem item) {items.remove(item);}public void place() {if (items.isEmpty()) {throw new IllegalStateException("Cannot place an order with no items.");}this.status = OrderStatus.PLACED;}// getters and other methods
}
基础设施层:提供数据持久化、消息传递等服务。包括数据库访问层、消息队列等。
public class JpaOrderRepository implements OrderRepository {private final EntityManager entityManager;public JpaOrderRepository(EntityManager entityManager) {this.entityManager = entityManager;}@Overridepublic void save(Order order) {entityManager.persist(order);}@Overridepublic Order findById(OrderId id) {return entityManager.find(Order.class, id);}
}
6.4 应用CQRS和事件驱动架构
在电商系统中,可以结合CQRS和事件驱动架构,实现读写操作的分离和事件处理的解耦。
CQRS:将订单的写操作和读操作分离,通过命令总线处理写操作,通过查询总线处理读操作。
事件驱动架构:在订单创建、支付完成、订单状态变更等关键操作时,发布领域事件,通过事件总线传递给订阅者,实现各服务之间的异步解耦。
// 订单创建事件
public class OrderCreatedEvent {private String orderId;private String userId;private LocalDateTime createTime;// 构造函数、getter和setter方法
}// 发布订单创建事件
public void placeOrder(OrderDTO orderDTO) {Order order = new Order(orderDTO);orderRepository.save(order);OrderCreatedEvent event = new OrderCreatedEvent(order.getId(), order.getUserId(), LocalDateTime.now());eventBus.publish(event);
}// 处理订单创建事件
public class OrderCreatedEventHandler {@Subscribepublic void handle(OrderCreatedEvent event) {// 处理订单创建后的业务逻辑}
}
7. 总结
DDD分层架构通过明确各层的职责和边界,实现了系统的高内聚、低耦合,提高了系统的可维护性和扩展性。在实际项目中,应用DDD分层架构需要结合具体的业务需求和技术环境,选择合适的分层策略,并结合CQRS和事件驱动架构等设计模式,实现灵活高效的系统设计。
通过对DDD分层架构的深入理解和实践应用,我们可以构建出更加健壮和灵活的系统,更好地应对复杂业务需求和快速变化的技术环境。