架构概览
Wow 框架是建立在四个基础范式之上的模块化、分层架构:领域驱动设计、CQRS、事件溯源和响应式编程。从 API 契约到底层存储后端,每个组件都设计为非阻塞 I/O、水平可扩展性和清晰的关注点分离。
Wow 的架构围绕一个核心原则构建:"领域模型即服务"。编写你的聚合,框架处理其余的一切 -- 命令路由、事件持久化、投影更新和 API 生成。开发者只需编写领域模型,框架自动提供命令路由、事件持久化、投影管道、OpenAPI 端点和分布式 Saga 编排。结果是一个业务逻辑存在于纯粹、可测试的聚合中,而基础设施关注点由可插拔的扩展模块处理的系统。
概览摘要
| 组件 | 职责 | 关键构件 | 源码 |
|---|---|---|---|
| wow-api | 纯 API 契约:CommandMessage、DomainEvent、AggregateId、NamedBoundedContext | wow-api 模块 | Wow.kt:26-45 |
| wow-core | 框架引擎:聚合、命令总线、事件存储、投影、Saga、序列化 | wow-core 模块 | CommandGateway.kt:75-178 |
| wow-spring | Spring 框架集成层 | wow-spring 模块 | settings.gradle.kts:32 |
| wow-spring-boot-starter | 具有功能特性的自动配置(Mongo、Kafka、Redis、R2DBC 等) | wow-spring-boot-starter 模块 | AggregateAutoConfiguration.kt:50-156 |
| wow-compiler | KSP 处理器:在编译时生成命令路由、事件元数据、OpenAPI 规范 | wow-compiler 模块 | settings.gradle.kts:26 |
| wow-test | 单元测试 DSL:AggregateSpec / SagaSpec 配合 Given-When-Expect 模式 | test/wow-test | settings.gradle.kts:44-45 |
| wow-kafka | 通过 Apache Kafka 实现的命令/事件总线 | wow-kafka 模块 | settings.gradle.kts:27 |
| wow-mongo / wow-redis / wow-r2dbc | 事件存储和快照存储后端 | wow-mongo、wow-redis、wow-r2dbc 模块 | settings.gradle.kts:28-30 |
| wow-elasticsearch | 通过 Elasticsearch 进行投影(读模型)存储 | wow-elasticsearch 模块 | settings.gradle.kts:31 |
| wow-opentelemetry | 端到端追踪和可观测性 | wow-opentelemetry 模块 | settings.gradle.kts:35 |
| wow-cosec | 授权和访问控制 | wow-cosec 模块 | settings.gradle.kts:40 |
| wow-webflux | Spring WebFlux 集成:自动注册命令路由处理函数 | wow-webflux 模块 | settings.gradle.kts:33 |
模块依赖图
框架遵循严格的分层架构,每个模块具有明确的依赖方向。wow-api 模块位于根层,定义了零外部依赖的纯契约,而基础设施模块在叶子层提供具体实现。
模块层级
模块层级定义在 settings.gradle.kts:19-63 中。每个模块仅依赖层级中位于它上方的模块,确保没有循环依赖。
分层详解
| 层 | 模块 | 描述 | 源码 |
|---|---|---|---|
| API 契约 | wow-api、wow-openapi | 纯 Kotlin 接口和数据类。零框架依赖。定义了 CommandMessage、DomainEvent、AggregateId、WaitStrategy 等。 | wow-api |
| 核心引擎 | wow-core | 聚合处理、命令总线、事件存储抽象、Saga 处理、投影分发、序列化。全部响应式(Project Reactor)。 | wow-core |
| 编译时 | wow-compiler | KSP 处理器。在编译时从注解生成命令路由表、事件处理器元数据和 OpenAPI 规范。 | settings.gradle.kts:26 |
| Spring 集成 | wow-spring、wow-spring-boot-starter | 将核心引擎桥接到 Spring 的 ApplicationContext。starter 通过 Gradle 功能变体提供自动配置和可选能力。 | WowAutoConfiguration.kt |
| 基础设施 | wow-kafka、wow-mongo、wow-redis、wow-r2dbc、wow-elasticsearch、wow-webflux | 核心抽象的具体实现。可通过类路径检测进行插拔。 | settings.gradle.kts:27-34 |
| 可观测性 | wow-opentelemetry | 通过 OpenTelemetry 进行端到端追踪、指标和日志集成。 | settings.gradle.kts:35 |
| 安全 | wow-cosec | 基于策略的访问控制的命令/查询授权。 | settings.gradle.kts:40 |
| 测试 | wow-test、wow-tck、wow-mock | 聚合和 Saga 测试 DSL;集成测试技术兼容性工具包;内存模拟实现。 | settings.gradle.kts:44-49 |
| 补偿 | wow-compensation-api、wow-compensation-core、wow-compensation-domain、wow-compensation-server | 事件补偿子系统,带有用于监控和重试失败事件的仪表板。 | settings.gradle.kts:56-63 |
通过模块分离强制执行的设计原则
- 依赖反转:核心模块依赖抽象(
CommandBus、EventStore、SnapshotRepository),而非具体实现。基础设施模块提供实现,并在运行时通过 Spring 的@ConditionalOnClass自动配置被发现。 - 开闭原则:可以添加新的存储后端或消息传输层作为新模块,而无需修改核心代码。
- 单一职责:每个模块只有一个变更理由。
wow-mongo处理 MongoDB 事件存储;wow-kafka处理 Kafka 消息传输;它们从不重叠。
关键设计决策
| 决策 | 理由 |
|---|---|
| 响应式(Project Reactor) | 非阻塞 I/O 实现最大吞吐量 |
| KSP 而非 KAPT | 编译时代码生成,构建速度更快 |
| Spring Boot 自动配置 | 零样板代码设置 |
| 可插拔的事件存储 | 无需更改领域代码即可切换后端 |
| Given-When-Expect 测试 | 可读、可维护的测试套件 |
| 暗启动支持 | 灰度发布的特性开关 |
命令处理流程
命令处理流程是 Wow 框架的中枢神经系统。它通过响应式管道协调命令路由、聚合加载、业务规则验证、事件持久化、快照创建和事件发布。
逐步流程描述
| 步骤 | 组件 | 操作 | 源码 |
|---|---|---|---|
| 1 | 客户端 | 发送带有 WaitStrategy 的命令,指定等待时长和等待阶段 | CommandGateway.kt:89-91 |
| 2 | CommandGateway | 实现 CommandBus 的入口点。根据聚合类型将命令路由到相应处理器 | CommandGateway.kt:75 |
| 3 | CommandDispatcher | 将命令总线桥接到聚合处理器过滤链;在 AggregateAutoConfiguration 中配置 | AggregateAutoConfiguration.kt:138-149 |
| 4 | AggregateProcessorFilter | 为目标聚合构造 AggregateProcessor,处理分片和重试逻辑 | AggregateAutoConfiguration.kt:91-96 |
| 5 | 快照 + 事件加载 | 加载最新快照,然后从 EventStore 重放增量事件以重建当前状态 | EventSourcingStateAggregateRepository.kt:41-60 |
| 6 | 业务规则执行 | 聚合根(CommandAggregate)验证不变量并执行命令处理函数 | SimpleCommandAggregate.kt:68-79 |
| 7 | 事件持久化 | EventStore.append() 原子写入事件流,通过版本检查强制执行乐观并发控制 | EventStore.kt:38-43 |
| 8 | 快照 + 发布 | 持久化后,保存快照并将领域事件发布到 EventBus 供下游处理 | AggregateAutoConfiguration.kt:100-106 |
等待策略与命令阶段
CommandGateway 支持在返回客户端之前等待命令到达特定的处理阶段。这对于解决 CQRS 架构中固有的读写同步延迟问题至关重要。
CommandStage 中的每个阶段都定义为一个枚举,具有显式的先决依赖关系:
| 阶段 | 先决条件 | 等待函数 | 典型用例 | 源码 |
|---|---|---|---|---|
SENT | (无) | 否 | 即发即忘命令;最大吞吐量 | CommandStage.kt:33 |
PROCESSED | SENT | 否 | 确保聚合已处理命令 | CommandStage.kt:43 |
SNAPSHOT | SENT、PROCESSED | 否 | 确保处理完成后已创建快照 | CommandStage.kt:52 |
PROJECTED | SENT、PROCESSED | 是 | 读模型已更新;解决同步延迟问题 | CommandStage.kt:63 |
EVENT_HANDLED | SENT、PROCESSED | 是 | 外部事件处理器已处理事件 | CommandStage.kt:73 |
SAGA_HANDLED | SENT、PROCESSED | 是 | Saga 编排器已完成处理 | CommandStage.kt:84 |
聚合生命周期
聚合根是 Wow 框架中领域逻辑的核心。它遵循明确定义的状态机来管理命令的处理方式、事件的溯源方式以及状态转换的发生方式。
聚合状态机
CommandState 枚举
CommandState 枚举 (CommandAggregate.kt:65-118) 管理聚合内命令处理的生命周期:
| 状态 | 有效操作 | 描述 | 源码 |
|---|---|---|---|
STORED | onSourcing(eventStream) | 聚合已准备好溯源事件。这是每个命令处理周期的入口点。 | CommandAggregate.kt:66-74 |
SOURCED | onStore(eventStore, eventStream) | 事件已应用到状态聚合。事件流已准备好持久化。 | CommandAggregate.kt:75-83 |
EXPIRED | (无) | 终端状态。不支持进一步操作。 | CommandAggregate.kt:84-85 |
关键生命周期规则
- 命令只能在
STORED状态下处理:聚合在溯源事件后转换到SOURCED,然后在持久化后回到STORED。这确保了每个聚合实例的串行命令处理,防止竞态条件,如 AggregateProcessor.kt:41-43 中所述。 - 删除和恢复是内置命令:
DefaultDeleteAggregate将聚合转换到已删除状态,而DefaultRecoverAggregate将其恢复。在已删除的聚合上尝试操作会抛出IllegalAccessDeletedAggregateException。 - 乐观并发控制:
EventStore.append()方法在检测到版本冲突时拒绝写入,确保每个聚合只有一个并发写入可以成功。
事件溯源架构
Wow 实现了完整的事件溯源模式,其中聚合状态源自领域事件的有序序列,而不是直接作为关系数据库中的行持久化。
状态重建策略
EventSourcingStateAggregateRepository 编排此流程。其加载过程如下:
- 对于最新版本(tailVersion =
Int.MAX_VALUE),它首先尝试从SnapshotRepository加载。 - 如果不存在快照,则通过
StateAggregateFactory创建新的聚合实例。 - 从聚合的预期下一个版本开始,按顺序应用
EventStore中的事件。
这种方法支持时间点状态重建:通过指定 tailVersion 或 tailEventTime,仓库可以重建聚合在任何历史时刻的状态。
事件存储接口
EventStore 接口 (EventStore.kt:27-98) 定义了事件持久化的契约:
| 方法 | 描述 | 并发保证 |
|---|---|---|
append(eventStream) | 原子追加领域事件流 | 版本冲突时抛出 EventVersionConflictException;重复 ID 时抛出 DuplicateAggregateIdException;重复请求时抛出 DuplicateRequestIdException |
load(aggregateId, headVersion, tailVersion) | 加载版本范围内的事件流(含两端) | 返回 Flux 用于响应式流式处理 |
load(aggregateId, headEventTime, tailEventTime) | 加载时间范围内的事件流(含两端) | 返回 Flux 用于响应式流式处理 |
single(aggregateId, version) | 加载特定版本的单个事件流 | 使用 load() 的便捷方法 |
last(aggregateId) | 加载最新的事件流 | 用于尾部版本查找 |
扩展点与可插拔性
Wow 框架自始至终遵循策略模式:每个基础设施关注点在 wow-core 中定义为接口,具体实现在扩展模块中提供。Spring 的自动配置在启动时根据类路径可用性装配适当的实现。
核心扩展接口
| 扩展点 | 接口 | 用途 | 实现 | 源码 |
|---|---|---|---|---|
| 命令总线 | CommandBus / DistributedCommandBus | 将命令路由到聚合处理器 | InMemoryCommandBus、LocalFirstCommandBus、Kafka 支持 | CommandBus.kt:36-69 |
| 事件总线 | EventBus / DomainEventBus | 将领域事件分发给投影、Saga 和处理器 | InMemoryEventBus、Kafka 支持、Redis 支持 | settings.gradle.kts:27 |
| 事件存储 | EventStore | 事件流的持久化存储 | MongoDB、Redis、R2DBC (PostgreSQL/MySQL/MariaDB) | EventStore.kt:27 |
| 快照仓库 | SnapshotRepository | 用于聚合性能优化的快照存储 | MongoDB、Redis、R2DBC | settings.gradle.kts:28-30 |
| 等待策略 | WaitStrategy | 控制命令响应时机 | WaitingForSent、WaitingForProcessed、WaitingForProjected 等 | CommandStage.kt:25-123 |
| ID 生成器 | IdGenerator (via CosId) | 生成全局唯一的聚合 ID | 雪花、号段等(通过 CosId 集成) | me.ahoo.cosid |
| 序列化 | MessageSerializer | 带类型元数据的 JSON 序列化 | 基于 Jackson 的 JsonSerializer | wow-core 序列化 |
Spring Boot 自动配置结构
wow-spring-boot-starter 模块使用 Gradle 功能变体声明可选能力,确保你只依赖所需的功能。关键的自动配置类包括:
| 自动配置类 | 条件 | 装配内容 | 源码 |
|---|---|---|---|
WowAutoConfiguration | @ConditionalOnWowEnabled | ServiceProvider、NamedBoundedContext、ErrorConverterRegistrar | WowAutoConfiguration.kt:37-72 |
AggregateAutoConfiguration | @ConditionalOnWowEnabled | StateAggregateFactory、StateAggregateRepository、CommandAggregateFactory、AggregateProcessorFactory、CommandDispatcher、过滤链 | AggregateAutoConfiguration.kt:50-156 |
EventAutoConfiguration | @ConditionalOnWowEnabled | 事件总线、事件分发器、事件处理器注册表 | EventAutoConfiguration.kt |
KafkaAutoConfiguration | @ConditionalOnKafkaEnabled | Kafka 命令总线、Kafka 事件总线 | KafkaAutoConfiguration.kt |
MongoEventSourcingAutoConfiguration | @ConditionalOnMongoEnabled | MongoDB 事件存储、MongoDB 快照仓库 | MongoEventSourcingAutoConfiguration.kt |
WebFluxAutoConfiguration | @ConditionalOnWebfluxEnabled | 命令路由处理函数、OpenAPI 端点 | WebFluxAutoConfiguration.kt |
CQRS 分离实战
CQRS 模式嵌入在架构的每一层:
写端职责
- 通过
CommandGateway接受命令 - 在聚合根内验证业务规则
- 产生代表状态变更的领域事件
- 通过原子事件存储追加维护强事务一致性
读端职责
- 通过
EventBus订阅领域事件 - 投影将事件转换为优化的读模型(Elasticsearch 索引、SQL 视图)
- Saga 通过发送后续命令响应事件,实现分布式事务编排
- 查询服务从专门构建的读模型(而非事件存储)读取数据
桥梁:状态事件
在写端和读端之间,Wow 引入了状态事件(StateEvent)。在命令处理后,框架将聚合的完整当前状态作为事件发布。这支持:
- 投影从完整的状态快照而非增量变化重建读模型
- 商业智能管道直接将聚合状态消费到数据仓库
- 通过 CoCache 进行缓存预热,实现超低延迟的查询服务
编译时代码生成(wow-compiler)
wow-compiler 模块是一个 KSP 处理器,消除了样板代码和运行时反射。在编译时,它:
- 扫描
@CommandRoute、@OnEvent、@OnStateEvent和其他 Wow 注解 - 生成 命令路由表、事件处理器注册表和函数元数据
- 生成 基于命令和事件模型的 OpenAPI 规范
这种编译时方法意味着:
- 无运行时注解扫描开销
- 更快的启动时间
- 在构建时验证的类型安全命令路由
编译器与 wow-openapi 集成以自动生成 OpenAPI 规范,并与 wow-schema 集成以为命令和事件生成 JSON Schema 定义。
性能特性
Wow 框架的架构选择直接支撑了其性能表现。影响性能的关键设计决策:
- 响应式(非阻塞)管道:全程使用
Mono和Flux确保无线程阻塞,在高负载下实现高并发。 - 快照优化:
SnapshotRepository避免了每次加载聚合时重放完整的事件历史。 - 本地优先路由:
LocalFirstCommandBus首先将命令路由到本地聚合处理器,仅在必要时回退到分布式路由。 - 等待策略灵活性:
SENT等待模式在即发即忘场景下达到 59,000+ TPS,而PROCESSED模式以吞吐量换取更强的一致性保证,达到 18,000+ TPS。
相关页面
| 页面 | 描述 |
|---|---|
| 简介 | Wow 框架特性和价值主张概览 |
| 领域建模 | 如何设计聚合根、命令和事件 |
| 命令网关 | 深入了解命令发送和等待策略 |
| 事件溯源 | 事件存储、快照和状态重建机制 |
| Saga 编排 | 通过 Saga 支持分布式事务 |
| 投影 | 构建和更新读模型 |
| 测试 | AggregateSpec 和 SagaSpec 测试 DSL |
| Spring Boot 集成 | 自动配置详情和属性参考 |
| CoCache | 用于查询性能的投影缓存 |
| 可观测性 | OpenTelemetry 追踪和指标 |