mcpField
S
spring-modulith

spring-modulith

github.com/claude-dev-suite开发4670 次安装2 天前更新
代码仓库

关于 spring-modulith

这是一项使用 Spring Modulith 设计模块化单体的技能,涵盖模块边界、公开 API 和基于事件的通信。它适用于希望将 Spring 应用组织为有界上下文,并检查架构与模块交互的开发者。

主要功能

  1. 01模块边界与允许的依赖
  2. 02公开 API 与内部包分离
  3. 03基于事件的模块间通信
  4. 04架构验证与模块测试
  5. 05事件持久化和重试考量

适用场景

  1. 01将 Spring 应用组织为模块化单体
  2. 02限制对模块内部实现的访问
  3. 03通过应用事件连接模块
  4. 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