feat: add snapshot
Description đĻĢ
Add snapshot capabilities :
export const pokemonsEventStoreWithSnapshot = new EventStore({
eventStoreId: 'POKEMONS',
eventTypes: [pokemonAppearedEvent, pokemonCaughtEvent, pokemonLeveledUpEvent],
reducer: pokemonsReducer,
eventStorageAdapter: eventStorageAdapterMock,
snapshotConfig: {
currentReducerVersion: 'v1.0.0',
shouldSaveSnapshot: createShouldSaveForRecurentSnapshots(5),
cleanUpAfterSnapshotSave: cleanUpLastSnapshot,
},
snapshotStorageAdapter,
});
consult packages/core/src/eventStore/eventStore.unit.test.ts and packages/core/src/eventStore/eventStore.fixtures.test.ts to know more.
Fixes https://github.com/castore-dev/castore/issues/181
Replaces https://github.com/castore-dev/castore/pull/174
TODO đ§
- [ ] Challenge
getAggregatesignature cf Question part - [ ] Propagate changes to
ConnectedEventStore - [ ] Add
DynamoDbSnapshotStorageAdapter - [ ] Add documentation
Questions âī¸
I'm not sure about getAggregate signature with snapshot. Currently getAggregate returns { aggregate, events, lastEvent };. But with snapshot returning events and lastEvent is either cost expensive or misleading.
Here are some propositions:
- include snapshot capabilities to current
getAggregate, determine if snapshot must be use ifsnapshotConfigis defined. Return partialsevents(only those fetched) andlastEventonly if one event have been fetched additionally to a snapshot -
(â
currently implemented) include snapshot capabilities to current
getAggregate, add an explicituseSnapshotoption, determine if snapshot must be use ifuseSnapshotistrue. Return partialsevents(only those fetched) andlastEventonly if one event have been fetched additionally to a snapshot - include snapshot capabilities to
getAggregatebut with signature BREAKING CHANGE. Determine if snapshot must be use ifsnapshotConfigis defined. Return onlyaggregate. Add a new explicitgetEventsAndAggregatecorresponding to currentgetAggregatewithout snapshot capabilities - include snapshot capabilities to a new explicit
getAggregateWithSnapshot. Determine if snapshot must be use ifsnapshotConfigis defined. Return onlyaggregate. LetgetAggregateas it is.
Type of change đ
Please delete options that are not relevant.
- [x] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [x] This change requires a documentation update
How Has This Been Tested? đ§âđŦ
đ§
- [x] Test unit
- [ ] Test linked on real projet
Test Configuration: đ§
- Firmware version:
- Hardware:
- Toolchain:
- SDK:
Checklist: â
- [ ] My code follows the style guidelines of this project
- [ ] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [ ] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my feature works
- [ ] New and existing unit tests pass locally with my changes
- [ ] Any dependent changes have been merged and published in downstream modules
@CorentinDoue What about:
interface EventStoreContext {
eventStoreId: string
}
interface SnapshotsQueryOptions {
aggregateId?: string
minVersion?: number
maxVersion? number
limit?: number
reverse?: boolean
reducerVersion?: string
pageToken?: string
}
interface SnapshotKey {
aggregateId: string
version: number
reducerVersion: string
}
interface SnapshotStorageAdapter {
listSnapshots( // <= Or maybe split between two methods: `listAllSnapshots` + `listAggregateSnapshots` ?
context: EventStoreContext,
options: SnapshotsQueryOptions = {}
) => Promise<{ snapshotsKeys: SnapshotKey[], nextPageToken?: string; }>
getSnapshot(snapshotKey: SnapshotKey, context: EventStoreContext) => Promise<{ snapshot: Aggregate }> // + possibly some useful metadata ?
putSnapshot(reducerVersion: string, snapshot: Aggregate, context: EventStoreContext) => Promise<void>
deleteSnapshot(snapshotKey: SnapshotKey, context: EventStoreContext) => Promise<void>
}
And then for event stores:
export const pokemonsEventStoreWithSnapshot = new EventStore({
...
reducerVersion: "v1.0.0", // <= I still think it's useful to version reducers outside of snapshots
snapshotStrategy: {
strategy: "PERIODIC", // or "NONE" or "CUSTOM"
periodInVersions: 100,
pruningStrategy: { // optional pruning strategy
strategy: "ON_NEW_SNAPSHOT", // or "NONE" or "TTL" for instance
keepLastVersions: 3
}
},
snapshotStorageAdapter,
});
Regarding getAggregate: I would hope that I could enable snapshots and benefit from them throughout my codebase without having to change all invocations. I personally would definitely prefer a breaking change that removes events from the return type, over having to add useSnapshot: true to all calls.
@oxc Yes the idea is that adding a snapshotStorageAdapter + a snapshotStrategy inside your EventStore is enough to enable snapshot without having to worry about it. The EventStore will then search for snapshot but still work if none is found.