一、架构演进概述
随着业务复杂度增加,软件架构也在不断演进:
架构演进历程:
- 分层架构 → 六边形架构 → 整洁架构 → 微服务架构
- 核心目标:实现高内聚、低耦合
架构质量评估:
- 独立性:框架、数据库、UI的可替换性
- 可测试性:业务逻辑可独立测试
- 可维护性:代码易于理解和修改
二、六边形架构(Hexagonal Architecture)
1. 核心理念
六边形架构,又称端口与适配器(Ports and Adapters),由Alistair Cockburn提出。
核心思想:
- 业务逻辑处于核心位置
- 通过端口与外部世界交互
- 适配器负责协议转换
2. 架构图
┌─────────────────────────────────┐
│ 外部世界 │
│ (数据库、Web UI、外部服务) │
└─────────────┬───────────────────┘
│
┌─────────────▼───────────────────┐
│ 适配器层 │
│ ┌─────────────┬─────────────┐ │
│ │ 驱动适配器 │ 驱动适配器 │ │
│ │ (Primary) │ (Primary) │ │
│ └──────┬──────┴──────┬──────┘ │
└─────────┼─────────────┼────────┘
│ │
┌─────────▼─────────────▼─────────┐
│ 端口层 │
│ (输入端口) (输出端口) │
│ ┌─────────────┬─────────────┐ │
│ │ │ │ │
│ └──────┬──────┴──────┬──────┘ │
└─────────┼─────────────┼────────┘
│ │
┌─────────▼─────────────▼─────────┐
│ 应用层 │
│ (用例/服务编排) │
└─────────┬─────────────────────┘
│
┌─────────▼─────────────────────┐
│ 领域层 │
│ (核心业务逻辑、实体、值对象) │
└─────────────────────────────────┘
3. 核心概念
端口(Port):
- 定义了系统与外部交互的方式
- 分为输入端口(驱动端口)和输出端口(被驱动端口)
- 端口是接口,不包含实现
适配器(Adapter):
- 负责实现端口定义的接口
- 处理外部协议和格式转换
- 适配器可以随时替换
4. 代码实现
领域层(核心):
java
// 领域实体
public class Order {
private OrderId id;
private CustomerId customerId;
private OrderStatus status;
private List<OrderItem> items;
public void addItem(Product product, int quantity) {
if (status != OrderStatus.DRAFT) {
throw new BusinessException("只有草稿状态可以添加商品");
}
items.add(new OrderItem(product, quantity));
}
public void submit() {
if (items.isEmpty()) {
throw new BusinessException("订单不能为空");
}
this.status = OrderStatus.SUBMITTED;
}
}
// 领域服务
public class OrderDomainService {
public void validateOrder(Order order) {
// 业务规则验证
}
}
输入端口(Driving Port):
java
// 输入端口接口(由应用层或领域层定义)
public interface OrderInputPort {
OrderDTO createOrder(CreateOrderCommand command);
OrderDTO updateOrder(Long orderId, UpdateOrderCommand command);
void cancelOrder(Long orderId);
OrderDTO getOrder(Long orderId);
Page<OrderDTO> listOrders(OrderQuery query);
}
输出端口(Driven Port):
java
// 输出端口接口(由应用层定义,由基础设施实现)
public interface OrderRepository {
Order findById(OrderId id);
Order save(Order order);
void delete(OrderId id);
}
public interface ProductRepository {
Product findById(ProductId id);
List<Product> findByIds(List<ProductId> ids);
}
public interface EventPublisher {
void publish(DomainEvent event);
}
public interface IdGenerator {
OrderId generateId();
}
应用层(用例):
java
// 应用服务实现输入端口
@Service
@Transactional
public class OrderApplicationService implements OrderInputPort {
private final OrderRepository orderRepository;
private final ProductRepository productRepository;
private final IdGenerator idGenerator;
private final EventPublisher eventPublisher;
public OrderApplicationService(
OrderRepository orderRepository,
ProductRepository productRepository,
IdGenerator idGenerator,
EventPublisher eventPublisher) {
this.orderRepository = orderRepository;
this.productRepository = productRepository;
this.idGenerator = idGenerator;
this.eventPublisher = eventPublisher;
}
@Override
public OrderDTO createOrder(CreateOrderCommand command) {
// 创建订单
Order order = Order.create(idGenerator.generateId(),
CustomerId.of(command.getCustomerId()));
// 添加商品
for (OrderItemDTO itemDTO : command.getItems()) {
Product product = productRepository.findById(
ProductId.of(itemDTO.getProductId()));
order.addItem(product, itemDTO.getQuantity());
}
// 提交订单
order.submit();
// 保存
orderRepository.save(order);
// 发布事件
eventPublisher.publish(new OrderCreatedEvent(order));
return toDTO(order);
}
}
适配器层:
java
// Web适配器(Driving Adapter)
@RestController
public class OrderController implements OrderInputPort {
private final OrderApplicationService orderService;
@PostMapping("/orders")
public ResponseEntity<OrderDTO> createOrder(@RequestBody CreateOrderRequest request) {
CreateOrderCommand command = toCommand(request);
OrderDTO result = orderService.createOrder(command);
return ResponseEntity.ok(result);
}
// ... 其他接口方法
}
// JPA适配器(Driven Adapter)
@Repository
public class JpaOrderRepository implements OrderRepository {
@Autowired
private OrderJpaRepository jpaRepository;
@Override
public Order findById(OrderId id) {
return jpaRepository.findById(id.getValue())
.map(this::toDomain)
.orElseThrow(() -> new EntityNotFoundException("Order not found"));
}
@Override
public Order save(Order order) {
OrderEntity entity = toEntity(order);
jpaRepository.save(entity);
return order;
}
}
// 消息适配器(Driven Adapter)
@Component
public class KafkaEventPublisher implements EventPublisher {
@Autowired
private KafkaTemplate<String, Object> kafkaTemplate;
@Override
public void publish(DomainEvent event) {
kafkaTemplate.send("domain-events", event.getClass().getSimpleName(), event);
}
}
// UUID适配器(Driven Adapter)
@Component
public class UuidIdGenerator implements IdGenerator {
@Override
public OrderId generateId() {
return OrderId.of(UUID.randomUUID().toString());
}
}
三、整洁架构(Clean Architecture)
1. 核心理念
整洁架构由Robert C. Martin提出,强调:
- 依赖规则:外层依赖内层,内层不知道外层
- 层次分离:明确的职责边界
- 独立性强:业务逻辑不依赖任何外部框架
2. 架构层次
┌─────────────────────────────────────────────────────────────────┐
│ 外层:框架 & 驱动 │
│ (Web框架、数据库、UI、外部接口、测试框架) │
├─────────────────────────────────────────────────────────────────┤
│ 接口适配器层 │
│ (Controllers、Gateways、Presenters) │
├─────────────────────────────────────────────────────────────────┤
│ 应用层 │
│ (用例、命令、查询、DTO) │
├─────────────────────────────────────────────────────────────────┤
│ 领域层 │
│ (实体、值对象、领域服务、领域事件、接口) │
├─────────────────────────────────────────────────────────────────┤
│ 内层:企业业务规则 │
└─────────────────────────────────────────────────────────────────┘
▲
│ 依赖方向
│
▼
3. 依赖规则
依赖规则(Dependency Rule):
- 源码依赖只能从外向内
- 内层不知道外层的存在
- 外层通过接口依赖内层
- 具体的实现细节放在外层
4. 代码结构
com.example.cleanarchitecture
├── domain # 领域层(最内层)
│ ├── model
│ │ ├── Order.java
│ │ ├── Product.java
│ │ └── Customer.java
│ ├── service
│ │ └── OrderDomainService.java
│ ├── event
│ │ └── DomainEvent.java
│ └── port # 端口(接口)
│ ├── inbound
│ │ ├── CreateOrderUseCase.java
│ │ └── OrderQueryService.java
│ └── outbound
│ ├── OrderRepository.java
│ ├── ProductRepository.java
│ └── EventPublisher.java
├── application # 应用层
│ ├── usecase
│ │ ├── CreateOrderUseCaseImpl.java
│ │ └── GetOrderUseCaseImpl.java
│ ├── dto
│ │ ├── CreateOrderCommand.java
│ │ └── OrderDTO.java
│ └── mapper
│ └── OrderMapper.java
├── adapter # 适配器层
│ ├── inbound
│ │ ├── web
│ │ │ └── OrderController.java
│ │ └── grpc
│ │ └── OrderGrpcAdapter.java
│ └── outbound
│ ├── persistence
│ │ ├── JpaOrderRepository.java
│ │ └── JpaProductRepository.java
│ ├── messaging
│ │ └── KafkaEventPublisher.java
│ └── external
│ └── PaymentGatewayAdapter.java
└── configuration # 配置层
└── DependencyInjectionConfig.java
5. 整洁架构实现
领域层:
java
// 领域实体
public class Order {
private OrderId id;
private CustomerId customerId;
private OrderStatus status;
private List<OrderItem> items;
public static Order create(OrderId id, CustomerId customerId) {
return new Order(id, customerId, OrderStatus.DRAFT, new ArrayList<>());
}
public void addItem(Product product, int quantity) {
// 业务规则
if (status != OrderStatus.DRAFT) {
throw new DomainException("只有草稿订单可以添加商品");
}
items.add(new OrderItem(product, quantity));
}
}
// 输入端口(用例接口)
public interface CreateOrderUseCase {
OrderDTO execute(CreateOrderCommand command);
}
// 输出端口(仓储接口)
public interface OrderRepository {
Optional<Order> findById(OrderId id);
Order save(Order order);
void delete(OrderId id);
List<Order> findByCustomerId(CustomerId customerId);
}
应用层:
java
// 用例实现
@Service
public class CreateOrderUseCaseImpl implements CreateOrderUseCase {
private final OrderRepository orderRepository;
private final ProductRepository productRepository;
private final IdGenerator idGenerator;
public CreateOrderUseCaseImpl(
OrderRepository orderRepository,
ProductRepository productRepository,
IdGenerator idGenerator) {
this.orderRepository = orderRepository;
this.productRepository = productRepository;
this.idGenerator = idGenerator;
}
@Override
public OrderDTO execute(CreateOrderCommand command) {
// 1. 验证客户
CustomerId customerId = CustomerId.of(command.getCustomerId());
// 2. 创建订单
OrderId orderId = idGenerator.generateId();
Order order = Order.create(orderId, customerId);
// 3. 添加商品
for (OrderItemCommand itemCmd : command.getItems()) {
Product product = productRepository.findById(ProductId.of(itemCmd.getProductId()))
.orElseThrow(() -> new DomainException("商品不存在"));
order.addItem(product, itemCmd.getQuantity());
}
// 4. 提交订单
order.submit();
// 5. 保存
orderRepository.save(order);
// 6. 返回DTO
return OrderDTO.from(order);
}
}
适配器层:
java
// Web控制器(入站适配器)
@RestController
@RequestMapping("/api/orders")
public class OrderController {
private final CreateOrderUseCase createOrderUseCase;
private final GetOrderUseCase getOrderUseCase;
@PostMapping
public ResponseEntity<OrderDTO> createOrder(@RequestBody @Valid CreateOrderRequest request) {
CreateOrderCommand command = CreateOrderCommand.from(request);
OrderDTO result = createOrderUseCase.execute(command);
return ResponseEntity.status(HttpStatus.CREATED).body(result);
}
@GetMapping("/{id}")
public ResponseEntity<OrderDTO> getOrder(@PathVariable Long id) {
OrderDTO result = getOrderUseCase.execute(id);
return ResponseEntity.ok(result);
}
}
// JPA仓储(出站适配器)
@Repository
public class JpaOrderRepository implements OrderRepository {
@Autowired
private OrderJpaRepository jpaRepository;
@Override
public Optional<Order> findById(OrderId id) {
return jpaRepository.findById(id.getValue())
.map(entity -> /* 转换为领域对象 */);
}
@Override
public Order save(Order order) {
OrderEntity entity = toEntity(order);
jpaRepository.save(entity);
return order;
}
}
四、架构对比
1. 六边形 vs 整洁架构
| 维度 | 六边形架构 | 整洁架构 |
|---|---|---|
| 核心理念 | 端口与适配器 | 依赖规则 |
| 层次划分 | 端口/适配器/应用/领域 | 实体/用例/接口适配器/框架 |
| 适用场景 | 轻量级应用 | 复杂企业应用 |
| 学习曲线 | 较平缓 | 较陡峭 |
| 灵活性 | 高 | 非常高 |
2. 分层架构对比
| 维度 | 传统三层 | 六边形 | 整洁架构 |
|---|---|---|---|
| 可测试性 | 差 | 好 | 非常好 |
| 可维护性 | 一般 | 好 | 非常好 |
| 耦合度 | 高 | 低 | 最低 |
| 复杂度 | 低 | 中 | 高 |
五、实战选择
1. 何时使用六边形
适合场景:
- 微服务内部设计
- 需要与多个外部系统交互
- 希望保持业务逻辑的纯粹性
- 团队对DDD有一定了解
2. 何时使用整洁架构
适合场景:
- 复杂的企业级应用
- 对可测试性要求极高
- 需要长期维护的项目
- 团队有较强的架构能力
3. 实际项目建议
java
// 项目结构建议(根据复杂度选择)
// 简单项目:六边形架构
com.example
├── domain
│ ├── model
│ └── port # 端口定义
├── application # 用例
└── adapter # 适配器
// 复杂项目:整洁架构
com.example
├── domain
│ ├── model
│ ├── service
│ └── port
├── application
│ ├── usecase
│ └── dto
└── adapter
├── inbound
└── outbound
六、测试策略
1. 领域层测试
java
// 领域实体测试(无需Spring,纯单元测试)
class OrderTest {
@Test
void shouldAddItemToDraftOrder() {
// Given
Order order = Order.create(OrderId.of("123"), CustomerId.of("456"));
// When
order.addItem(Product.of("P1", Money.of(100)), 2);
// Then
assertEquals(1, order.getItems().size());
assertEquals(Money.of(200), order.getTotalAmount());
}
@Test
void shouldRejectAddItemToSubmittedOrder() {
// Given
Order order = Order.create(OrderId.of("123"), CustomerId.of("456"));
order.submit();
// When/Then
assertThrows(BusinessException.class,
() -> order.addItem(Product.of("P1", Money.of(100)), 2));
}
}
2. 用例层测试
java
// 用例测试(使用Mock)
@ExtendWith(MockitoExtension.class)
class CreateOrderUseCaseTest {
@Mock
private OrderRepository orderRepository;
@Mock
private ProductRepository productRepository;
@Mock
private IdGenerator idGenerator;
@InjectMocks
private CreateOrderUseCaseImpl useCase;
@Test
void shouldCreateOrderSuccessfully() {
// Given
CreateOrderCommand command = CreateOrderCommand.builder()
.customerId("C123")
.items(List.of(new OrderItemCommand("P1", 2)))
.build();
when(productRepository.findById(any())).thenReturn(Optional.of(Product.of("P1", Money.of(100))));
when(idGenerator.generateId()).thenReturn(OrderId.of("O123"));
// When
OrderDTO result = useCase.execute(command);
// Then
assertNotNull(result);
verify(orderRepository).save(any());
}
}
七、总结
架构设计的选择建议:
- 六边形架构:适合微服务、对接多外部系统
- 整洁架构:适合复杂业务、需要高可测试性
- 核心原则:业务逻辑与外部依赖解耦
实施建议:
- 从六边形架构开始
- 随着业务复杂度增加演进到整洁架构
- 重视领域模型的设计
- 保持架构的持续演进
个人观点,仅供参考