castore-dev / castore

Making Event Sourcing easy 😎
MIT License
230 stars 19 forks source link

Auto increment version if event with same version exists, but content is different #180

Closed katesclau closed 7 months ago

katesclau commented 10 months ago

Is your feature request related to a problem? Please describe. Hi :wave:

We have been using Castore for a while, and it's been pretty great, but as the system scales, we've encountered the scenario where multiple sources would issue events for the same eventStore, with the same version (we use timestamp). This may be an issue caused by using timestamp as version, but as an audit trail, the decision made sense back then.

In this scenario, the EventStore fails to record the second event due to ConditionalCheckFailedException (we are using DynamoDB). Our solution is to catch the ConditionalCheckFailedException, and validate that the new event is unique, if so, increase the version number and proceed with EventStore.pushEvent

Describe the solution you'd like Could we add an auto-increment option to EventPusher options parameter?

export declare type EventPusher<EVENT_DETAILS extends EventDetail, $EVENT_DETAILS extends EventDetail, AGGREGATE extends Aggregate, $AGGREGATE extends Aggregate> = (event: $EVENT_DETAILS extends infer $EVENT_DETAIL ? Omit<$EVENT_DETAIL, 'timestamp'> : never, options?: {
    prevAggregate?: $AGGREGATE;
    autoIncreaseVersion?: boolean;
}) => Promise<{
    event: EVENT_DETAILS;
    nextAggregate?: AGGREGATE;
}>;

Describe alternatives you've considered Implement idempotency validation on the event storage adapter level, validating if the event exists, and if the content is unique.

Additional context Our current solution sits at the application level and handles the version autoincrement. For our architecture, this makes sense, as the EventStore sits behind our event bus, but I may be able to support efforts to a more generic solution :wink:

Thanks to the Castore team :bow:

ThomasAribart commented 9 months ago

Hello!

You should definitely use the Command class for your write operations: https://castore-dev.github.io/castore/docs/event-sourcing/pushing-events/

Commands are designed to be retried in case of concurrency, that should solve your pb entirely. You can increase the eventAlreadyExistsRetries property if needed (defaulted to 2)