clean-architecture 是做什么的?
为Java 21+和Spring Boot 3.5+应用提供整洁架构、六边形架构(端口与适配器)以及领域驱动设计的实现模式。适用于分层架构设计、领域逻辑与框架解耦、端口与适配器实现、实体/值对象/聚合创建,或重构单体代码库以提升可测试性和可维护性。
概述
该技能提供了一套经过验证的模式,帮助你在基于Java 21+和Spring Boot 3.5+的项目中构建整洁且可持续的架构。它涵盖了整洁架构、六边形架构(端口与适配器)以及领域驱动设计(DDD)的核心概念,并提供了具体的代码示例和最佳实践。
核心原则
- 依赖反转:高层模块不应依赖低层模块,两者都应依赖抽象。抽象不应依赖细节,细节应依赖抽象。
- 领域隔离:将领域逻辑与基础设施、框架和外部关注点完全分离。
- 测试性:通过解耦和接口抽象,使领域层和应用层能够轻松进行单元测试。
- 可维护性:通过清晰的架构边界和代码组织,降低变更成本和引入缺陷的风险。
架构层
| 层级 | 职责 | 典型组件 |
|---|---|---|
| 领域层 | 核心业务逻辑和规则 | 实体、值对象、聚合、领域服务、仓储接口 |
| 应用层 | 用例编排、事务管理、权限控制 | 应用服务、命令/查询处理器、DTO |
| 基础设施层 | 与外部系统的集成 | 仓储实现、消息客户端、数据库访问、REST控制器 |
技术栈
- 语言:Java 21+(推荐使用记录类型、密封类、模式匹配等特性)
- 框架:Spring Boot 3.5+
- 构建工具:Maven 或 Gradle
- 数据库:JPA/Hibernate、Spring Data JDBC、MyBatis 等
- 测试:JUnit 5、Mockito、ArchUnit(用于架构约束)
快速开始
1. 初始化项目结构
src/
main/
java/
com/example/
domain/
model/
entity/
valueobject/
aggregate/
service/
repository/
application/
service/
port/
inbound/
outbound/
dto/
infrastructure/
adapter/
inbound/
rest/
message/
outbound/
persistence/
messaging/
config/
2. 定义领域实体
public record OrderId(UUID id) {}
public class Order {
private OrderId id;
private CustomerId customerId;
private List<OrderLineItem> items;
private OrderStatus status;
// 业务行为封装在领域实体中
public void addItem(Product product, int quantity) {
// 业务规则验证
}
public void confirm() {
// 状态转换逻辑
}
}
3. 实现端口与适配器
输出端口(由领域层定义,基础设施层实现)
// domain/repository/OrderRepository.java
public interface OrderRepository {
Optional<Order> findById(OrderId id);
void save(Order order);
void delete(OrderId id);
}
// infrastructure/adapter/outbound/persistence/JpaOrderRepository.java
@Component
public class JpaOrderRepository implements OrderRepository {
private final SpringDataJpaOrderRepository springRepo;
private final OrderMapper mapper;
@Override
public Optional<Order> findById(OrderId id) {
return springRepo.findById(id.id())
.map(mapper::toDomain);
}
@Override
public void save(Order order) {
var entity = mapper.toEntity(order);
springRepo.save(entity);
}
}
4. 编写应用服务
// application/service/OrderApplicationService.java
@Service
@Transactional
public class OrderApplicationService {
private final OrderRepository orderRepository;
private final DomainEventPublisher eventPublisher;
public OrderId createOrder(CreateOrderCommand command) {
var order = new Order(OrderId.generate(), command.customerId());
command.items().forEach(item -> order.addItem(item.product(), item.quantity()));
orderRepository.save(order);
eventPublisher.publish(new OrderCreatedEvent(order.id()));
return order.id();
}
}
高级模式
事件驱动架构
// 领域事件定义
public record OrderConfirmedEvent(OrderId orderId, Instant occurredOn) implements DomainEvent {}
// 事件处理器
@Component
public class OrderConfirmedHandler {
@EventListener
public void handle(OrderConfirmedEvent event) {
// 处理事件,如发送通知、更新库存等
}
}
CQRS 模式
// 命令
public record CreateOrderCommand(CustomerId customerId, List<OrderItemDto> items) {}
// 查询(返回视图模型)
public record OrderSummaryView(OrderId id, CustomerId customerId, BigDecimal total, OrderStatus status) {}
// 查询处理器
@Component
public class OrderQueryHandler {
private final OrderReadRepository readRepository;
public OrderSummaryView getOrderSummary(OrderId id) {
return readRepository.findSummaryById(id);
}
}
架构测试
使用ArchUnit强制执行架构约束:
@ArchTest
static final ArchRule domain_should_not_depend_on_infrastructure =
classes()
.that().resideInAPackage("..domain..")
.should().onlyDependOnClassesThat()
.resideInAnyPackage("java..", "..domain..");
@ArchTest
static final ArchRule application_should_only_depend_on_domain =
classes()
.that().resideInAPackage("..application..")
.should().onlyDependOnClassesThat()
.resideInAnyPackage("java..", "..domain..", "..application..");
最佳实践
- 保持领域层纯净:领域层不应导入任何Spring或框架注解。
- 使用接口定义边界:输出端口由领域层定义,输入端口由应用层定义。
- 避免贫血模型:将业务逻辑尽可能封装在领域实体和值对象中。
- 小聚合:每个聚合应尽量小,以维持一致性和性能。
- 显式依赖:应用服务通过构造器注入明确声明所需依赖。
- 使用ArchUnit:自动化验证架构规则,防止退化。
常见陷阱
- 领域层泄漏到基础设施层
- 过于复杂的聚合导致性能问题
- 忽略值对象的不可变性
- 依赖注入泛滥导致设计退化
参考资料
- Robert C. Martin,《整洁架构》
- Eric Evans,《领域驱动设计》
- Alistair Cockburn,《六边形架构》
- Spring官方文档
许可证
MIT