关于 spring-modulith
这是一项使用 Spring Modulith 设计模块化单体的技能,涵盖模块边界、公开 API 和基于事件的通信。它适用于希望将 Spring 应用组织为有界上下文,并检查架构与模块交互的开发者。
主要功能
- 01模块边界与允许的依赖
- 02公开 API 与内部包分离
- 03基于事件的模块间通信
- 04架构验证与模块测试
- 05事件持久化和重试考量
适用场景
- 01将 Spring 应用组织为模块化单体
- 02限制对模块内部实现的访问
- 03通过应用事件连接模块
- 04检查模块边界与循环依赖
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ claude-dev-suite/ claude-dev-suite --skill spring-modulith安装后在 Claude Code 中输入/spring-modulith确认已加载。
附带文件advanced.md
详细介绍
Spring Modulith
完整参考:事件外部化(Outbox)、模块 API 暴露、@ApplicationModuleTest、场景测试、架构验证、可观测性和渐进式拆分,请参阅 advanced.md。
概述
┌─────────────────────────────────────────────────────────────────┐
│ 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) │
│ │
└─────────────────────────────────────────────────────────────────┘
快速入门
<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.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;
// 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) {}
// Internal implementation (not accessible from other modules)
// order/internal/OrderRepository.java
@Repository
interface OrderRepository extends JpaRepository<Order, Long> {
List<Order> findByCustomerId(Long customerId);
}
通过事件进行模块间通信
// 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()));
}
}
}
// 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());
}
}
最佳实践
模块设计
// ✅ 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!
事件设计
// ✅ 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!
最佳实践表
| 应做 | 不应做 |
|---|---|
| 一个模块 = 一个限界上下文 | 混合无关的关注点 |
| 在根包中放置公共 API | 暴露内部类 |
在 internal/ 中放置实现 | 从外部访问内部内容 |
| 通过事件进行通信 | 直接跨模块调用 |
| 使用不可变事件(record) | 使用可变事件对象 |
生产环境检查清单
- 定义模块边界
- 正确限定内部包的范围
- 基于事件的通信
- 架构验证测试
- 配置事件持久化
- 失败事件重试机制
- 生成文档
- 无循环依赖
- 共享内核保持精简
不适合使用此技能的情况
- 简单应用 - 会带来不必要的复杂性
- 现有微服务 - 已经拆分
- 紧密耦合的单体应用 - 需要先进行大量重构
- 小型团队 - 可能不需要正式的边界
反模式
| 反模式 | 问题 | 解决方案 |
|---|---|---|
| 循环依赖 | 模块相互引用 | 使用事件或共享内核 |
| 暴露内部类 | 包结构不正确 | 将其移至 internal/ 包 |
| 未发布事件 | 缺少事务 | 验证 @Transactional |
| 事件丢失 | 未进行持久化 | 使用 spring-modulith-events-jpa |
| 回调事件 | 事件要求回调 | 在事件中包含所有数据 |
| 暴露仓库 | 耦合紧密 | 将仓库保留在内部 |
快速故障排查
| 问题 | 诊断 | 修复 |
|---|---|---|
| 循环依赖 | 运行 modules.verify() | 重构为使用事件 |
| 违反内部访问规则 | 检查包结构 | 将类移至适当位置 |
| 未收到事件 | 检查监听器 | 验证 @EventListener 注解 |
| 独立运行时测试失败 | 检查依赖项 | 使用适当的 BootstrapMode |
| 事件发布失败 | 检查事务 | 确保存在 @Transactional |
参考文档
评论
支持 Markdown