spring-modulith
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseSpring Modulith
Spring Modulith
Full Reference: See advanced.md for Event Externalization (Outbox), Module API Exposure, @ApplicationModuleTest, Scenario Testing, Architecture Verification, Observability, and Gradual Decomposition.
完整参考:请查看[advanced.md]了解事件外部化(Outbox)、模块API暴露、@ApplicationModuleTest、场景测试、架构验证、可观测性以及逐步分解相关内容。
Overview
概述
┌─────────────────────────────────────────────────────────────────┐
│ Spring Modulith Application │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Order │ │ Payment │ │ Inventory │ │
│ │ Module │──▶│ Module │◀──│ Module │ │
│ ├──────────────┤ ├──────────────┤ ├──────────────┤ │
│ │ order/ │ │ payment/ │ │ inventory/ │ │
│ │ ├─ api/ │ │ ├─ api/ │ │ ├─ api/ │ │
│ │ │ (public) │ │ │ (public) │ │ │ (public) │ │
│ │ └─ internal/ │ │ └─ internal/ │ │ └─ internal/ │ │
│ │ (private) │ │ (private) │ │ (private) │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │ │ │ │
│ └───────────────────┴───────────────────┘ │
│ Event Bus (Async) │
│ │
└─────────────────────────────────────────────────────────────────┘┌─────────────────────────────────────────────────────────────────┐
│ Spring Modulith Application │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Order │ │ Payment │ │ Inventory │ │
│ │ Module │──▶│ Module │◀──│ Module │ │
│ ├──────────────┤ ├──────────────┤ ├──────────────┤ │
│ │ order/ │ │ payment/ │ │ inventory/ │ │
│ │ ├─ api/ │ │ ├─ api/ │ │ ├─ api/ │ │
│ │ │ (public) │ │ │ (public) │ │ │ (public) │ │
│ │ └─ internal/ │ │ └─ internal/ │ │ └─ internal/ │ │
│ │ (private) │ │ (private) │ │ (private) │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │ │ │ │
│ └───────────────────┴───────────────────┘ │
│ Event Bus (Async) │
│ │
└─────────────────────────────────────────────────────────────────┘Quick Start
快速开始
xml
<!-- pom.xml -->
<dependency>
<groupId>org.springframework.modulith</groupId>
<artifactId>spring-modulith-starter-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.modulith</groupId>
<artifactId>spring-modulith-starter-test</artifactId>
<scope>test</scope>
</dependency>src/main/java/com/example/ecommerce/
├── EcommerceApplication.java # Root package
├── order/ # Order module
│ ├── Order.java # Public API
│ ├── OrderService.java # Public API
│ ├── OrderCreatedEvent.java # Public event
│ └── internal/ # Internal implementation
│ ├── OrderRepository.java
│ └── OrderValidator.java
├── payment/ # Payment module
│ ├── PaymentService.java
│ └── internal/
└── shared/ # Shared kernel (minimal!)
└── Money.javaxml
<!-- pom.xml -->
<dependency>
<groupId>org.springframework.modulith</groupId>
<artifactId>spring-modulith-starter-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.modulith</groupId>
<artifactId>spring-modulith-starter-test</artifactId>
<scope>test</scope>
</dependency>src/main/java/com/example/ecommerce/
├── EcommerceApplication.java # 根包
├── order/ # 订单模块
│ ├── Order.java # 公开API
│ ├── OrderService.java # 公开API
│ ├── OrderCreatedEvent.java # 公开事件
│ └── internal/ # 内部实现
│ ├── OrderRepository.java
│ └── OrderValidator.java
├── payment/ # 支付模块
│ ├── PaymentService.java
│ └── internal/
└── shared/ # 共享核心(尽可能精简!)
└── Money.javaModule Structure
模块结构
java
// Package-info to document module
// order/package-info.java
@org.springframework.modulith.ApplicationModule(
displayName = "Order Management",
allowedDependencies = {"payment", "inventory::InventoryService"}
)
package com.example.ecommerce.order;java
// Public API (root package)
@Service
@RequiredArgsConstructor
@Transactional
public class OrderService {
private final OrderRepository orderRepository;
private final ApplicationEventPublisher events;
public Order createOrder(CreateOrderRequest request) {
Order order = Order.create(request.customerId(), request.items());
order = orderRepository.save(order);
// Publish event for other modules
events.publishEvent(new OrderCreatedEvent(order.getId(), order.getTotal()));
return order;
}
public void confirmOrder(Long orderId) {
Order order = orderRepository.findById(orderId)
.orElseThrow(() -> new OrderNotFoundException(orderId));
order.confirm();
orderRepository.save(order);
events.publishEvent(new OrderConfirmedEvent(orderId));
}
}
// Public event
public record OrderCreatedEvent(Long orderId, Money total) {}java
// Internal implementation (not accessible from other modules)
// order/internal/OrderRepository.java
@Repository
interface OrderRepository extends JpaRepository<Order, Long> {
List<Order> findByCustomerId(Long customerId);
}java
// 用于文档化模块的Package-info
// order/package-info.java
@org.springframework.modulith.ApplicationModule(
displayName = "Order Management",
allowedDependencies = {"payment", "inventory::InventoryService"}
)
package com.example.ecommerce.order;java
// 公开API(根包)
@Service
@RequiredArgsConstructor
@Transactional
public class OrderService {
private final OrderRepository orderRepository;
private final ApplicationEventPublisher events;
public Order createOrder(CreateOrderRequest request) {
Order order = Order.create(request.customerId(), request.items());
order = orderRepository.save(order);
// 为其他模块发布事件
events.publishEvent(new OrderCreatedEvent(order.getId(), order.getTotal()));
return order;
}
public void confirmOrder(Long orderId) {
Order order = orderRepository.findById(orderId)
.orElseThrow(() -> new OrderNotFoundException(orderId));
order.confirm();
orderRepository.save(order);
events.publishEvent(new OrderConfirmedEvent(orderId));
}
}
// 公开事件
public record OrderCreatedEvent(Long orderId, Money total) {}java
// 内部实现(其他模块不可访问)
// order/internal/OrderRepository.java
@Repository
interface OrderRepository extends JpaRepository<Order, Long> {
List<Order> findByCustomerId(Long customerId);
}Inter-Module Communication via Events
基于事件的模块间通信
java
// Payment module listens to Order module events
// payment/internal/OrderEventHandler.java
@Component
@RequiredArgsConstructor
@Slf4j
class OrderEventHandler {
private final PaymentService paymentService;
@EventListener
public void onOrderCreated(OrderCreatedEvent event) {
log.info("Order created: {}, processing payment", event.orderId());
paymentService.initiatePayment(event.orderId(), event.total());
}
}
// payment/PaymentService.java
@Service
@RequiredArgsConstructor
public class PaymentService {
private final PaymentRepository paymentRepository;
private final ApplicationEventPublisher events;
public void initiatePayment(Long orderId, Money amount) {
Payment payment = Payment.create(orderId, amount);
payment = paymentRepository.save(payment);
processPaymentAsync(payment);
}
@Async
void processPaymentAsync(Payment payment) {
try {
payment.confirm();
paymentRepository.save(payment);
events.publishEvent(new PaymentConfirmedEvent(payment.getOrderId(), payment.getId()));
} catch (PaymentFailedException e) {
payment.fail(e.getMessage());
paymentRepository.save(payment);
events.publishEvent(new PaymentFailedEvent(payment.getOrderId(), e.getMessage()));
}
}
}java
// Order module reacts to Payment events
// order/internal/PaymentEventHandler.java
@Component
@RequiredArgsConstructor
class PaymentEventHandler {
private final OrderService orderService;
@EventListener
public void onPaymentConfirmed(PaymentConfirmedEvent event) {
orderService.confirmOrder(event.orderId());
}
@EventListener
public void onPaymentFailed(PaymentFailedEvent event) {
orderService.cancelOrder(event.orderId(), event.reason());
}
}java
// 支付模块监听订单模块事件
// payment/internal/OrderEventHandler.java
@Component
@RequiredArgsConstructor
@Slf4j
class OrderEventHandler {
private final PaymentService paymentService;
@EventListener
public void onOrderCreated(OrderCreatedEvent event) {
log.info("订单已创建:{},正在处理支付", event.orderId());
paymentService.initiatePayment(event.orderId(), event.total());
}
}
// payment/PaymentService.java
@Service
@RequiredArgsConstructor
public class PaymentService {
private final PaymentRepository paymentRepository;
private final ApplicationEventPublisher events;
public void initiatePayment(Long orderId, Money amount) {
Payment payment = Payment.create(orderId, amount);
payment = paymentRepository.save(payment);
processPaymentAsync(payment);
}
@Async
void processPaymentAsync(Payment payment) {
try {
payment.confirm();
paymentRepository.save(payment);
events.publishEvent(new PaymentConfirmedEvent(payment.getOrderId(), payment.getId()));
} catch (PaymentFailedException e) {
payment.fail(e.getMessage());
paymentRepository.save(payment);
events.publishEvent(new PaymentFailedEvent(payment.getOrderId(), e.getMessage()));
}
}
}java
// 订单模块响应支付事件
// order/internal/PaymentEventHandler.java
@Component
@RequiredArgsConstructor
class PaymentEventHandler {
private final OrderService orderService;
@EventListener
public void onPaymentConfirmed(PaymentConfirmedEvent event) {
orderService.confirmOrder(event.orderId());
}
@EventListener
public void onPaymentFailed(PaymentFailedEvent event) {
orderService.cancelOrder(event.orderId(), event.reason());
}
}Best Practices
最佳实践
Module Design
模块设计
java
// ✅ DO: Expose only what's needed
@ApplicationModule(allowedDependencies = {"shared"})
package com.example.ecommerce.order;
// ✅ DO: Communicate via events
events.publishEvent(new OrderCreatedEvent(orderId));
// ✅ DO: Use records for immutable events
public record OrderCreatedEvent(Long orderId, Money total) {}
// ❌ DON'T: Circular dependencies
// order → payment → order // WRONG!
// ❌ DON'T: Expose repositories
public interface OrderRepository { } // Should not be public
// ❌ DON'T: Direct access to internal
@Autowired
OrderValidator validator; // From another module - WRONG!java
// ✅ 推荐:仅暴露必要内容
@ApplicationModule(allowedDependencies = {"shared"})
package com.example.ecommerce.order;
// ✅ 推荐:通过事件通信
events.publishEvent(new OrderCreatedEvent(orderId));
// ✅ 推荐:使用record实现不可变事件
public record OrderCreatedEvent(Long orderId, Money total) {}
// ❌ 避免:循环依赖
// order → payment → order // 错误!
// ❌ 避免:暴露仓库
public interface OrderRepository { } // 不应公开
// ❌ 避免:直接访问内部类
@Autowired
OrderValidator validator; // 从其他模块注入 - 错误!Event Design
事件设计
java
// ✅ DO: Events with all necessary data
public record OrderCreatedEvent(
Long orderId,
Long customerId,
Money total,
List<OrderItem> items,
Instant createdAt
) {}
// ❌ DON'T: Events requiring callback
public record OrderCreatedEvent(Long orderId) {}
// Consumer must call orderService.getOrder(orderId) - WRONG!java
// ✅ 推荐:包含所有必要数据的事件
public record OrderCreatedEvent(
Long orderId,
Long customerId,
Money total,
List<OrderItem> items,
Instant createdAt
) {}
// ❌ 避免:需要回调的事件
public record OrderCreatedEvent(Long orderId) {}
// 消费者必须调用orderService.getOrder(orderId) - 错误!Best Practices Table
最佳实践表格
| Do | Don't |
|---|---|
| One module = one bounded context | Mix unrelated concerns |
| Public API in root package | Expose internal classes |
Implementation in | Access internal from outside |
| Communicate via events | Direct cross-module calls |
| Use immutable events (records) | Mutable event objects |
| 推荐做法 | 避免做法 |
|---|---|
| 一个模块对应一个限界上下文 | 混合不相关的关注点 |
| 公开API放在根包 | 暴露内部类 |
实现代码放在 | 从外部访问内部内容 |
| 通过事件进行通信 | 直接跨模块调用 |
| 使用不可变事件(record) | 可变事件对象 |
Production Checklist
生产环境检查清单
- Module boundaries defined
- Internal packages properly scoped
- Event-based communication
- Architecture verification tests
- Event persistence configured
- Failed event retry mechanism
- Documentation generated
- No circular dependencies
- Shared kernel minimal
- 已定义模块边界
- 内部包已正确限定范围
- 采用基于事件的通信方式
- 已配置架构验证测试
- 已配置事件持久化
- 已配置失败事件重试机制
- 已生成文档
- 无循环依赖
- 共享核心尽可能精简
When NOT to Use This Skill
不适用场景
- Simple applications - Unnecessary complexity
- Existing microservices - Already decomposed
- Tightly coupled monoliths - Requires significant refactoring first
- Small teams - May not need formal boundaries
- 简单应用 - 引入不必要的复杂度
- 现有微服务 - 已完成拆分
- 紧耦合单体应用 - 需先进行大量重构
- 小型团队 - 可能不需要正式的边界定义
Anti-Patterns
反模式
| Anti-Pattern | Problem | Solution |
|---|---|---|
| Circular dependency | Modules reference each other | Use events or shared kernel |
| Internal class exposed | Wrong package structure | Move to |
| Event not published | Missing transaction | Verify @Transactional |
| Event lost | No persistence | Use spring-modulith-events-jpa |
| Callback events | Events require calling back | Include all data in event |
| Exposing repositories | Tight coupling | Keep repositories internal |
| 反模式 | 问题 | 解决方案 |
|---|---|---|
| 循环依赖 | 模块互相引用 | 使用事件或共享核心 |
| 内部类被暴露 | 包结构错误 | 移至 |
| 事件未发布 | 缺失事务 | 验证@Transactional注解 |
| 事件丢失 | 无持久化 | 使用spring-modulith-events-jpa |
| 回调事件 | 事件需要回调 | 在事件中包含所有必要数据 |
| 暴露仓库 | 紧耦合 | 保持仓库为内部访问 |
Quick Troubleshooting
快速故障排查
| Problem | Diagnostic | Fix |
|---|---|---|
| Circular dependency | Run modules.verify() | Refactor to use events |
| Internal access violation | Check package structure | Move classes appropriately |
| Event not received | Check listener | Verify @EventListener annotation |
| Test fails in isolation | Check dependencies | Use appropriate BootstrapMode |
| Event publication fails | Check transaction | Ensure @Transactional present |
| 问题 | 诊断方法 | 修复方案 |
|---|---|---|
| 循环依赖 | 运行modules.verify() | 重构为使用事件通信 |
| 内部访问违规 | 检查包结构 | 调整类的位置 |
| 事件未被接收 | 检查监听器 | 验证@EventListener注解 |
| 隔离测试失败 | 检查依赖 | 使用合适的BootstrapMode |
| 事件发布失败 | 检查事务 | 确保存在@Transactional注解 |