NEventStore: Sagas, Commands and not Losing Them

1k Views Asked by At

NEventStore: 5.1
Simple setup: WebApp (Asp.NET 4.5) == command-side

I'm searching for the "right" way for not losing commands, with an eye on sagas/process-managers which maybe would wait endlessly for an event produced from a command that was actually never handled.

Old: Dispatchers

I initially used sync commands, but with an eye on sagas/process-managers I thought it would be safer to first store them an then get them through SyncDispatcher (or AsyncDispatcher). Otherwise, that's my concern, if a saga would try to send a command and the command didn't finish due to app-crash/powerloss/..., it would be lost and noone would know.

So I created a command-stream and appended each command to that. The IsDispatched showed, if that command was already handled.
That worked.

PollingClient and Command-Stream

Now that the dispatchers are obsolete, I switched to PollingClient. What I lost is the Dispatched information.

A startup-issue arose:
I naively started polling from the current latest checkpoint going forward, but when the application restarted there was a chance that commands were stored but not executed before the crash and therefore lost (that actually happened).

I just came across the idea:
store the basic outcome of commands as (non-domain-)events in another stream.
This stream would contain CommandSucceeded and CommandFailed events.
Whenever the application starts the latest command-id or command-checkpoint-number gets extracted used to load the commands right after that one...

Questions

  • Are my concerns, that sync command-handling leads to the danger of losing a saga-generated command, wrong? If yes, why?
  • Is this generally a good idea: one big command stream?
  • Is this generally a good idea: store generic command-outcome-events in a stream?
2

There are 2 best solutions below

2
MikeSW On

Afaik NEventStore is meant to be the storage for event sourcing i.e storing domain objects as a stream of events. Commands and sagas have nothing to do with it. It's your service bus which should take care of durability and saga management.

Personally, I treat the event store simply as a repository detail. The application service (command handler) will dispatch the generated events, after they've been persisted.

If the app crashes and the service bus is durable (not a memory one) then the event/command will be handled again automatically, because the service bus should detect if a message wasn't successfully handled. Of course, your message handlers should be idempotent for that reason.

6
Andrea Balducci On

You can:

  1. Store you command in a command queue | persistent log
  2. Use command id (guid) as Commit Id on NEventStore
  3. Mark your command as executed in your Command Handler | Pipeline Hook | Polling Client

NEventStore gives you idempotency on same AggregateId (streamid) + CommitId, so if you app crashes before the command is marked as processed and you replay your command, the resulting commits are automatically discarded by NES.