envato / event_sourcery

A library for building event sourced applications in Ruby
MIT License
84 stars 10 forks source link

created_at not available to processors. #196

Closed berkes closed 6 years ago

berkes commented 6 years ago

Let's say we want a created_at-field in our OutstandingTodo Projection.

Currently there are two ways to achieve this, all have their downsides:

  1. Inject a created_at into the event when building it, "manually".
  2. Have the projector take the created_at when set, else fall back to DateTime.now: outstanding_todo.created_at = event.created_at || DateTime.now

The first implies that we manually set a created_at all over the place. This could be refactored into a generic BaseCommand, but that feels clumsy and adds more complexity then needed.

The second can cause different times when replaying than on initial insert. On replay, the event_time will be used, on initial insert the DateTime.now will be used. Due to the async nature, that could very well be seconds difference.

Is this by design? Are there other workarounds? Do I misunderstand the use and meaning of event.created_at?

If not, I'd gladly whack up a patch that moves the filling of a created_at to initialize instead of in the sink. But that might mean that a created_at is set to (micro)seconds before it was actually created.

twe4ked commented 6 years ago

Is this by design?

Yes. There are a few reasons for this:

  1. When sending a command you want to store the time that the event happened generally not the time that the command got accepted. Consider the case where a command is coming from a background worker and may not be sent immediately or may not succeed the first time. The event has still already happened so it's best to include a timestamp in the event body.
  2. Another thing to consider is that ideally we're trying to use language that relates as closely as possible to the actual domain. For instance a BloodPressureMeasured event could have multiple timestamps associated with it, taken_at and recorded_at that more closely represent the data.

Have the projector take the created_at when set […]

The event.created_at should always be set by Event Sourcery but should be considered metadata about the event. As you noted with the fallback to DateTime.now, ideally you don't want to ever generate data inside a projector as the event store will never have the information and therefore your projections wouldn't be deterministic.

berkes commented 6 years ago

Thanks for the clear explanation!

For future reference and for others looking, here's my reasoning and solution, after the explanation above:

As you noted with the fallback to DateTime.now, ideally you don't want to ever generate data inside a projector

This is what I thought too. Indeed because of:

as the event store will never have the information and therefore your projections wouldn't be deterministic.

So, my NodeProposed event gets a timestamp that is next to created_at, but fits in the domain. In my case a proposed_at. This is then used in the ProposedNodes projection instead of relying on a created_at.

twe4ked commented 6 years ago

No worries :)