evtstore
Version:
Event Sourcing with Node.JS
87 lines (66 loc) • 3.25 kB
Markdown
It is important to pass in the `Stream` as a literal to provide the foundation for `Domain.createHandler` intellisense
```ts
type AggregateOptions = {
stream: string
create: () => Aggreate
fold: (event: Event, previous: Aggregate) => Partial<Aggregate>
/**
* Used by the aggregate persistence logic - This is to determine whether hydration is okay or not
* This must be provided to enable aggregate persistence
*/
version?: string
/**
* Whether or not to persist the aggregate when appending events.
* `version` must be provided as well
*/
persistAggregate?: boolean
}
function createAggreate<Event, Aggregate, Stream extends string>(
options: AggregateOptions
): StorableAggregate<Event, Aggregate, Stream>
```
### Aggregate Persistence
⚠️⚠️⚠️ **WARNING: FOOTGUN AHEAD** ⚠️⚠️⚠️
Using this feature can lead to bugs when not this feature is not used as intended.
It is incredibly important to update the `version` property passed to `createAggregate` when modifying the `fold` function.
If you modify the `fold` function, but forget to modify the `version`, this framework will use the persisted aggregate instead of _re-folding_ the aggregate.
For large domains or where performance _really_ matters, the aggregate can be persisted within events to faster aggregate hydration.
Enable this feature when calling `createAggregate(...)` by passing `.version` and `.persistAggregate: true`.
The `.version` value is recorded in the persisted copy of the aggregate and later used to determine if you have updated your version.
**IMPORTANT**: When making (breaking) changes to the `fold` function, it is important to increase the `.version` value:
- Doing so will force the aggregate to be re-calculate
- The re-calculation occurs because of a mismatch between the `.version` and the version on the persisted copy of the aggregate
- **Unexpected behaviour can occur if the `fold` function is modified, but the `createAggregate({ version })` is not**
### createDomain
The `Domain` is created separately to allow command handlers to import the domain and retrieve any aggregates it may need to process a command.
Simply import the domain in your command handler module whenever it is needed.
```ts
type DomainOptions = {
provider: Provider<any>
// Enable aggregate caching
// Only new events will be retrieved since the aggregate was last folded
useCache?: boolean
}
type AggregateTree = { [key: string]: StorableAggregate }
function createDomain(opts: DomainOptions, aggregates: AggregateTree): Domain
```
### createCommands
Command handlers are centered around appending new events to a single `Aggregate` event stream.
We import the `Domain` we created with `createDomain` and pass in an aggregate as the first argument of `createCommands`.
The second argument is an object comprised of command handler functions.
```ts
type CommandHandlers<Cmd> = {
[]]: (
aggregateId: string,
event: Event,
meta: EventMeta
) => Promise<Event | void>
}
import { domain } from '...'
function createCommands<Event, Aggregate, Command>(
aggregate: DomainAggregate,
handler: CommandHandlers
): CommandHandler
```