What the Compiler Generates
The Wow compiler is a KSP processor. It reads a domain module's annotated classes at build time and writes Kotlin sources and resources beside them, in build/generated/ksp/main/. Nothing is found by scanning or reflection when the service runs: the host loads what the compiler wrote. A model the compiler cannot bind fails the build with a message that names the function and the rule. This page lists what it writes and where each part is used.
For the eShop's ordering domain module, the compiler writes:
build/generated/ksp/main/
├── kotlin/me/ahoo/wow/example/eshop/ordering/domain/order/
│ ├── EshopOrderingDomainModelProvider.kt the binding index
│ ├── OrderModel.kt fun orderModel(context, paymentPolicy, configure)
│ ├── OrderProcessSagaSubscriptions.kt fun orderProcessSagaHandlers(dependencies)
│ └── OrderStateProperties.kt the state's query fields
└── resources/META-INF/
├── services/me.ahoo.wow.kernel.ModelProvider
└── wow/manifest/eshop-ordering-domain.json the Manifest fragmentModel factories
Each aggregate root gets a function named after it, <root>Model with a lower-case first letter: Order gives orderModel. Its parameters are the context, then one per domain service the command functions take, then an optional configure block on the model builder. A test builds the model with it directly: orderModel("eshop-ordering", PaymentPolicy(30.minutes)). The host passes the services from the application's beans.
Processor handlers
Each @Saga, @Projection or @Reactor class gets <processor>Handlers(dependencies): OrderProcessSaga gives orderProcessSagaHandlers. It builds the processor once from its constructor's dependencies and returns one binding per handler, named <processor>.<function>, with the saga's @OnRejected and @OnCommitted handlers attached. A processor test passes its fakes through Dependencies.of(...) (Test Domain Code).
Query fields
With wow-query-api on the module's class path, each state gets <State>Properties: one QueryField constant per path a snapshot query can name, such as ProductStateProperties.STATE__PRICE for state.price, beside the system fields of SnapshotProperties (Query Snapshots).
The binding index
Each module gets one class implementing ModelProvider, registered for ServiceLoader, named after the module: eshop-ordering-domain gives EshopOrderingDomainModelProvider. It names the module's context and fragment, builds its models and its processors from Dependencies, and the host loads every one on its class path. A test can load it to check what the module builds (Test Domain Code).
The Manifest fragment
The compiler writes the module's part of the service's contract to META-INF/wow/manifest/<module>.json: the context, its aggregates with their commands, events, lifecycle commands, query models and schedules, its subscriptions with what each derives, schedules and calls, its published streams and its error codes. Its type schemas are references until the Gradle plugin completes them with the JSON Schemas Jackson's rules give, the query keywords included. The Manifest and Compatibility covers what happens to it next.
The contract code of the api module
The plugin selects from the fragment what a peer needs, the commands, events, state schemas and errors of each context, and puts it in the api module's jar as META-INF/wow/contract.json. An api module also compiles <Context>Contract from it, such as EshopOrderingContract:
| Member | Holds |
|---|---|
NAME | The context's name |
<Aggregate>.TYPE, <Aggregate>.ref(tenant, id) | The aggregate's type, and a ref to one of its aggregates |
<Aggregate>.Errors.* | Its error codes, by their full names |
<Aggregate>.STATE__* | The query fields of its state, with wow-query-api |
A peer depends on the api module alone and writes no path, name or code by hand: EshopOrderingContract.Order.ref(tenant, id) addresses an order, and EshopOrderingContract.Order.Errors.WRONG_STATUS compares a rejection (Call Other Services, Rejections and Error Codes). The domain module itself compiles against a view of the api without this code, so it never depends on its own generated contract.