Spring Modulith应用中事件的放置位置:模块内还是共享模块?
Spring Modulith事件存放方案对比与建议
在Spring Modulith中处理事件时,两种存放方案各有优劣,核心要围绕模块自治性和长期可维护性来权衡:
方案一:事件放在对应模块内(推荐)
目录结构示例:
- com.example |- product |- application |- domain |- events |- Product.java |- ProductEvent.java |- package-info.java |- spi |- web |- order ...
优势:
- 贴合模块化设计原则:事件是模块业务逻辑的自然产物(比如
ProductEvent必然是Product模块内业务变化触发的),放在模块内让代码职责更清晰,维护时能直接在模块内找到事件的定义、触发逻辑,不用跨模块跳转。 - 避免shared模块膨胀:shared模块很容易沦为“万能垃圾桶”,什么都往里塞,最后变成难以维护的大杂烩。把事件留在各自模块,能让shared模块只承担真正跨模块共享的基础能力(比如通用工具类、基础DTO)。
- 适配Spring Modulith原生机制:Spring Modulith本身强调模块自治,通过
@ApplicationModule和@Exported可以精准控制模块对外暴露的事件,其他模块通过订阅机制消费事件,既实现松耦合,又保持模块边界清晰。
方案二:事件放在shared模块
目录结构示例:
- com.example |- product |- application |- domain |- spi |- web ... |- shared |- events |- product |- Product.java |- ProductEvent.java |- order |- Order.java |- OrderEvent.java ...
表面优势:
- 模块之间不直接依赖,都依赖shared模块,看起来实现了“完全解耦”;若未来要拆分微服务,事件定义已经在共享模块,不用调整依赖关系。
实际问题:
- 破坏模块自治:事件是模块业务的一部分,放在shared模块意味着模块的业务变更需要修改shared模块(比如新增事件、修改事件字段),违反了“模块对自己的产出负责”的原则,增加跨模块修改成本,还容易引发代码冲突。
- 维护成本高:所有模块的事件堆在shared模块,随着业务发展,shared会越来越臃肿,难以梳理每个事件的业务归属和触发逻辑,排查问题时要跨多个模块找事件源头,效率极低。
最终建议
优先选择将事件放在对应模块内,这更符合Spring Modulith的设计哲学。如果担心未来拆分微服务,可以通过以下方式优化:
- 在模块的
events包上添加@Exported注解,明确对外暴露的事件,其他模块通过Spring Modulith的订阅机制消费,无需依赖模块的其他部分。 - 事件类保持简洁,只包含必要的业务数据,避免依赖模块内的复杂对象,这样即使未来拆分,事件也能轻松迁移到独立的API模块(而非现在就提前塞到shared里)。
- 仅将真正跨模块共享的基础事件结构(比如通用
DomainEvent基类)放在shared模块,具体业务事件仍留在各自模块。
内容的提问来源于stack exchange,提问作者energize-cactus
相关产品推荐
相关产品推荐

