Architecture Overview
The Wow Framework is a modular, layered architecture built on four foundational paradigms: Domain-Driven Design, CQRS, Event Sourcing, and Reactive Programming. Every component from the API contracts down to the storage backends is designed for non-blocking I/O, horizontal scalability, and clean separation of concerns.
Wow's architecture is built around a single core principle: "Domain Model as a Service". Write your aggregate, and the framework handles everything else -- command routing, event persistence, projection updates, and API generation. Developers write only the domain model, and the framework automatically provides command routing, event persistence, projection pipelines, OpenAPI endpoints, and distributed saga orchestration. The result is a system where business logic lives in pure, testable aggregates, while infrastructure concerns are handled by pluggable extension modules.
At-a-Glance Summary
| Component | Responsibility | Key Artifact | Source |
|---|---|---|---|
| wow-api | Pure API contracts: CommandMessage, DomainEvent, AggregateId, NamedBoundedContext | wow-api module | Wow.kt:26-45 |
| wow-core | Framework engine: aggregates, command bus, event store, projections, sagas, serialization | wow-core module | CommandGateway.kt:75-178 |
| wow-spring | Spring Framework integration layer | wow-spring module | settings.gradle.kts:32 |
| wow-spring-boot-starter | Auto-configuration with feature capabilities (Mongo, Kafka, Redis, R2DBC, etc.) | wow-spring-boot-starter module | AggregateAutoConfiguration.kt:50-156 |
| wow-compiler | KSP processor: generates command routing, event metadata, OpenAPI specs at compile time | wow-compiler module | settings.gradle.kts:26 |
| wow-test | Unit testing DSL: AggregateSpec / SagaSpec with Given-When-Expect pattern | test/wow-test | settings.gradle.kts:44-45 |
| wow-kafka | Command/event bus implementation via Apache Kafka | wow-kafka module | settings.gradle.kts:27 |
| wow-mongo / wow-redis / wow-r2dbc | Event store and snapshot store backends | wow-mongo, wow-redis, wow-r2dbc modules | settings.gradle.kts:28-30 |
| wow-elasticsearch | Projection (read model) storage via Elasticsearch | wow-elasticsearch module | settings.gradle.kts:31 |
| wow-opentelemetry | End-to-end tracing and observability | wow-opentelemetry module | settings.gradle.kts:35 |
| wow-cosec | Authorization and access control | wow-cosec module | settings.gradle.kts:40 |
| wow-webflux | Spring WebFlux integration: auto-registers command route handler functions | wow-webflux module | settings.gradle.kts:33 |
Module Dependency Graph
The framework follows strict layered architecture where each module has a clear dependency direction. The wow-api module sits at the root, defining pure contracts with zero external dependencies, while infrastructure modules at the leaf level provide concrete implementations.
Module Hierarchy
The module hierarchy is defined in settings.gradle.kts:19-63. Every module depends only on modules above it in the hierarchy, ensuring no circular dependencies.
Layer Breakdown
| Layer | Modules | Description | Source |
|---|---|---|---|
| API Contracts | wow-api, wow-openapi | Pure Kotlin interfaces and data classes. Zero framework dependencies. Defines CommandMessage, DomainEvent, AggregateId, WaitStrategy, etc. | wow-api |
| Core Engine | wow-core | Aggregate processing, command bus, event store abstraction, saga processing, projection dispatch, serialization. All reactive (Project Reactor). | wow-core |
| Compile-Time | wow-compiler | KSP processor. Generates command routing tables, event handler metadata, and OpenAPI specs from annotations at compile time. | settings.gradle.kts:26 |
| Spring Integration | wow-spring, wow-spring-boot-starter | Bridges the core engine into Spring's ApplicationContext. The starter provides auto-configuration with Gradle feature variants for optional capabilities. | WowAutoConfiguration.kt |
| Infrastructure | wow-kafka, wow-mongo, wow-redis, wow-r2dbc, wow-elasticsearch, wow-webflux | Concrete implementations of core abstractions. Pluggable via classpath detection. | settings.gradle.kts:27-34 |
| Observability | wow-opentelemetry | End-to-end tracing, metrics, and logging integration via OpenTelemetry. | settings.gradle.kts:35 |
| Security | wow-cosec | Command/query authorization with policy-based access control. | settings.gradle.kts:40 |
| Testing | wow-test, wow-tck, wow-mock | Aggregate and saga testing DSL; Technology Compatibility Kit for integration tests; in-memory mock implementations. | settings.gradle.kts:44-49 |
| Compensation | wow-compensation-api, wow-compensation-core, wow-compensation-domain, wow-compensation-server | Event compensation subsystem with dashboard for monitoring and retrying failed events. | settings.gradle.kts:56-63 |
Design Principles Enforced by Module Separation
- Dependency Inversion: Core modules depend on abstractions (
CommandBus,EventStore,SnapshotRepository), not concrete implementations. Infrastructure modules provide the implementations and are discovered at runtime via Spring's@ConditionalOnClassauto-configuration. - Open-Closed Principle: New storage backends or message transports can be added as new modules without touching core code.
- Single Responsibility: Each module has exactly one reason to change.
wow-mongohandles MongoDB event storage;wow-kafkahandles Kafka message transport; they never overlap.
Key Design Decisions
| Decision | Rationale |
|---|---|
| Reactive (Project Reactor) | Non-blocking I/O for maximum throughput |
| KSP over KAPT | Compile-time code generation, faster builds |
| Spring Boot auto-configuration | Zero-boilerplate setup |
| Pluggable event store | Swap backends without changing domain code |
| Given-When-Expect testing | Readable, maintainable test suite |
| Dark launch support | Feature flags for gradual rollouts |
Command Processing Flow
The command processing flow is the central nervous system of the Wow Framework. It coordinates command routing, aggregate loading, business rule validation, event persistence, snapshot creation, and event publication through a reactive pipeline.
Step-by-Step Flow Description
| Step | Component | Action | Source |
|---|---|---|---|
| 1 | Client | Sends a command with a WaitStrategy specifying how long to wait and at what stage | CommandGateway.kt:89-91 |
| 2 | CommandGateway | Entry point implementing CommandBus. Routes command to the appropriate handler based on aggregate type | CommandGateway.kt:75 |
| 3 | CommandDispatcher | Bridges the command bus to the aggregate processor filter chain; configured in AggregateAutoConfiguration | AggregateAutoConfiguration.kt:138-149 |
| 4 | AggregateProcessorFilter | Constructs an AggregateProcessor for the target aggregate, handling sharding and retry logic | AggregateAutoConfiguration.kt:91-96 |
| 5 | Snapshot + Event Loading | Loads the latest snapshot, then replays incremental events from the EventStore to rebuild current state | EventSourcingStateAggregateRepository.kt:41-60 |
| 6 | Business Rule Execution | The aggregate root (CommandAggregate) validates invariants and executes the command handler function | SimpleCommandAggregate.kt:68-79 |
| 7 | Event Persistence | EventStore.append() atomically writes the event stream, enforcing optimistic concurrency via version checks | EventStore.kt:38-43 |
| 8 | Snapshot + Publish | After persistence, a snapshot is saved and domain events are published to the EventBus for downstream processing | AggregateAutoConfiguration.kt:100-106 |
Wait Strategies and Command Stages
The CommandGateway supports waiting for the command to reach specific processing stages before returning to the client. This is critical for solving the read-write synchronization delay problem inherent in CQRS architectures.
Each stage in CommandStage is defined as an enum with explicit prerequisite dependencies:
| Stage | Prerequisites | Waits for Functions | Typical Use Case | Source |
|---|---|---|---|---|
SENT | (none) | No | Fire-and-forget commands; maximum throughput | CommandStage.kt:33 |
PROCESSED | SENT | No | Ensure aggregate has processed the command | CommandStage.kt:43 |
SNAPSHOT | SENT, PROCESSED | No | Ensure snapshot has been created after processing | CommandStage.kt:52 |
PROJECTED | SENT, PROCESSED | Yes | Read model updated; solves sync delay problem | CommandStage.kt:63 |
EVENT_HANDLED | SENT, PROCESSED | Yes | External event handlers have processed the events | CommandStage.kt:73 |
SAGA_HANDLED | SENT, PROCESSED | Yes | Saga orchestrators have completed processing | CommandStage.kt:84 |
Aggregate Lifecycle
The aggregate root is the heart of domain logic in the Wow Framework. It follows a well-defined state machine that governs how commands are processed, events are sourced, and state transitions occur.
Aggregate State Machine
CommandState Enum
The CommandState enum (CommandAggregate.kt:65-118) governs the lifecycle of command processing within an aggregate:
| State | Valid Operations | Description | Source |
|---|---|---|---|
STORED | onSourcing(eventStream) | The aggregate is ready to source events. This is the entry point for each command processing cycle. | CommandAggregate.kt:66-74 |
SOURCED | onStore(eventStore, eventStream) | Events have been applied to the state aggregate. The event stream is ready to be persisted. | CommandAggregate.kt:75-83 |
EXPIRED | (none) | Terminal state. No further operations are supported. | CommandAggregate.kt:84-85 |
Key Lifecycle Rules
- Commands can only be processed in
STOREDstate: The aggregate transitions toSOURCEDafter sourcing events, then back toSTOREDafter persisting. This ensures serial command processing per aggregate instance, preventing race conditions, as documented in AggregateProcessor.kt:41-43. - Delete and Recover are built-in commands:
DefaultDeleteAggregatetransitions the aggregate to a deleted state, whileDefaultRecoverAggregaterestores it. Attempting operations on a deleted aggregate throwsIllegalAccessDeletedAggregateException. - Optimistic concurrency: The
EventStore.append()method rejects writes when a version conflict is detected, ensuring that only one concurrent write can succeed per aggregate.
Event Sourcing Architecture
Wow implements a full event sourcing pattern where the aggregate state is derived from the ordered sequence of domain events rather than being directly persisted as a row in a relational database.
State Rebuild Strategy
The EventSourcingStateAggregateRepository orchestrates this flow. Its loading process works as follows:
- For the latest version (tailVersion =
Int.MAX_VALUE), it first attempts to load from theSnapshotRepository. - If no snapshot exists, it creates a new aggregate instance via
StateAggregateFactory. - Events from the
EventStoreare applied sequentially starting from the aggregate's expected next version.
This approach enables point-in-time state reconstruction: by specifying a tailVersion or tailEventTime, the repository can rebuild the aggregate state as it existed at any historical point.
Event Store Interface
The EventStore interface (EventStore.kt:27-98) defines the contract for event persistence:
| Method | Description | Concurrency Guarantees |
|---|---|---|
append(eventStream) | Atomically appends a domain event stream | Throws EventVersionConflictException on version conflict; DuplicateAggregateIdException for duplicate IDs; DuplicateRequestIdException for duplicate requests |
load(aggregateId, headVersion, tailVersion) | Loads event streams within version range (inclusive) | Returns Flux for reactive streaming |
load(aggregateId, headEventTime, tailEventTime) | Loads event streams within time range (inclusive) | Returns Flux for reactive streaming |
single(aggregateId, version) | Loads a single event stream at a specific version | Convenience method using load() |
last(aggregateId) | Loads the most recent event stream | Used for tail version lookup |
Extension Points and Pluggability
The Wow Framework follows the Strategy Pattern throughout: every infrastructure concern is defined as an interface in wow-core, with concrete implementations in extension modules. Spring's auto-configuration wires the appropriate implementation at startup based on classpath availability.
Core Extension Interfaces
| Extension Point | Interface | Purpose | Implementations | Source |
|---|---|---|---|---|
| Command Bus | CommandBus / DistributedCommandBus | Routes commands to aggregate processors | InMemoryCommandBus, LocalFirstCommandBus, Kafka-backed | CommandBus.kt:36-69 |
| Event Bus | EventBus / DomainEventBus | Distributes domain events to projections, sagas, and handlers | InMemoryEventBus, Kafka-backed, Redis-backed | settings.gradle.kts:27 |
| Event Store | EventStore | Persistent storage of event streams | MongoDB, Redis, R2DBC (PostgreSQL/MySQL/MariaDB) | EventStore.kt:27 |
| Snapshot Repository | SnapshotRepository | Snapshot storage for aggregate performance optimization | MongoDB, Redis, R2DBC | settings.gradle.kts:28-30 |
| Wait Strategy | WaitStrategy | Controls command response timing | WaitingForSent, WaitingForProcessed, WaitingForProjected, etc. | CommandStage.kt:25-123 |
| ID Generator | IdGenerator (via CosId) | Generates globally unique aggregate IDs | Snowflake, segment, etc. (via CosId integration) | me.ahoo.cosid |
| Serialization | MessageSerializer | JSON serialization with type metadata | Jackson-based JsonSerializer | wow-core serialization |
Spring Boot Auto-Configuration Structure
The wow-spring-boot-starter module uses Gradle feature variants to declare optional capabilities, ensuring that you only depend on what you need. Key auto-configuration classes include:
| Auto-Configuration Class | Condition | Wires | Source |
|---|---|---|---|
WowAutoConfiguration | @ConditionalOnWowEnabled | ServiceProvider, NamedBoundedContext, ErrorConverterRegistrar | WowAutoConfiguration.kt:37-72 |
AggregateAutoConfiguration | @ConditionalOnWowEnabled | StateAggregateFactory, StateAggregateRepository, CommandAggregateFactory, AggregateProcessorFactory, CommandDispatcher, filter chain | AggregateAutoConfiguration.kt:50-156 |
EventAutoConfiguration | @ConditionalOnWowEnabled | Event bus, event dispatcher, event processor registry | EventAutoConfiguration.kt |
KafkaAutoConfiguration | @ConditionalOnKafkaEnabled | Kafka command bus, Kafka event bus | KafkaAutoConfiguration.kt |
MongoEventSourcingAutoConfiguration | @ConditionalOnMongoEnabled | MongoDB event store, MongoDB snapshot repository | MongoEventSourcingAutoConfiguration.kt |
WebFluxAutoConfiguration | @ConditionalOnWebfluxEnabled | Command routing handler functions, OpenAPI endpoints | WebFluxAutoConfiguration.kt |
CQRS Separation in Action
The CQRS pattern is embedded at every level of the architecture:
Write Side Responsibilities
- Accepts commands via
CommandGateway - Validates business rules within the aggregate root
- Produces domain events that represent state changes
- Maintains strong transactional consistency via atomic event store appends
Read Side Responsibilities
- Subscribes to domain events via the
EventBus - Projections transform events into optimized read models (Elasticsearch indices, SQL views)
- Sagas react to events by sending follow-up commands, enabling distributed transaction orchestration
- Query services read from purpose-built read models, not the event store
The Bridge: State Events
Between the write and read sides, Wow introduces state events (StateEvent). After command processing, the framework publishes the full current state of the aggregate as an event. This enables:
- Projections that rebuild read models from complete state snapshots rather than incremental deltas
- Business Intelligence pipelines that consume aggregate states directly into data warehouses
- Cache warming via CoCache for ultra-low-latency query services
Compile-Time Code Generation (wow-compiler)
The wow-compiler module is a KSP processor that eliminates boilerplate and runtime reflection. At compile time, it:
- Scans
@CommandRoute,@OnEvent,@OnStateEvent, and other Wow annotations - Generates command routing tables, event handler registries, and function metadata
- Produces OpenAPI specifications from command and event models
This compile-time approach means:
- No runtime annotation scanning overhead
- Faster startup time
- Type-safe command routing verified at build time
The compiler integrates with wow-openapi to automatically generate OpenAPI specs, and with wow-schema to produce JSON Schema definitions for commands and events.
Performance Characteristics
The architectural choices of the Wow Framework directly enable its performance profile. Key design decisions that impact performance:
- Reactive (non-blocking) pipelines:
MonoandFluxthroughout ensure no thread blocking, enabling high concurrency under load. - Snapshot optimization:
SnapshotRepositoryavoids replaying the full event history on every aggregate load. - Local-first routing:
LocalFirstCommandBusroutes commands to local aggregate processors first, falling back to distributed routing only when necessary. - Wait strategy flexibility:
SENTwait mode achieves 59,000+ TPS for fire-and-forget scenarios, whilePROCESSEDmode trades throughput for stronger consistency guarantees at 18,000+ TPS.
Related Pages
| Page | Description |
|---|---|
| Introduction | Overview of Wow framework features and value proposition |
| Domain Modeling | How to design aggregate roots, commands, and events |
| Command Gateway | Deep-dive into command sending and wait strategies |
| Event Sourcing | Event store, snapshots, and state rebuild mechanics |
| Saga Orchestration | Distributed transaction support via sagas |
| Projections | Building and updating read models |
| Testing | AggregateSpec and SagaSpec testing DSL |
| Spring Boot Integration | Auto-configuration details and property reference |
| CoCache | Projection caching for query performance |
| Observability | OpenTelemetry tracing and metrics |