2

This is a question for those times you are creating the read model by event handlers.

The question comes down to : How can a Event Handler draw from other sources of information than what is contained in the handled event itself to construct a read model?

I have two aggregates, Person and Address. A person can be associated to different addresses (Home, Work, etc.)

So I have a read model table Addresses (ID, line1, line2, line3, etc.)

I also have a read model table PersonAddresses (PersonID, AddressID, line1, line2, line3) - Duplicated, denormalized data.

  • When an Address is created, I dispatch an event AddressRegistered with the address info (ID, Line1, Line2, etc.).

  • When an Address is associated to a Person, I dispatch an event AddressAssociated (with the PersonId, AddressId).

Then, I want to create the readModel.

  • When I receive a AddressRegistered event, I add an entry into the Addresses read model table.

  • When I receive a AddressAssociated event, I want to add an entry into the PersonAddresses read model table.

Though, I would like to have the address info as well (Line1, Line2, Line3) in the PersonAddresses table as my clien application will use it this way. But this info is not contained in the AddressAssociated event. This event only contains PersonID and AddressId.

What is the most recommended way of getting that information (Line1, Line2, Line3) for the associated Address in the AddressAssociated event Handler in order to populate the PersonAdresses read model table?

  • In the domain, attach the address info (Line1, Line2, ...) to the AddressAssociated event so that it can be directly added into the PersonAddresses table ? That would require a domain service as it would need to look up multiple aggregates. (Address and Person are two aggregates)
  • When adding entries in the PersonAddresses, retrieve the information beforehand from the Addresses read model table? Is it recommended that the read model read itself (eventual consistency, etc.) ?
  • Something else?
6
  • Are you doing CQRS + Event Sourcing as well?
    – Erik Eidt
    Sep 8, 2016 at 14:59
  • @ErikEidt Yup! Modified the title :)
    – Ludovic C
    Sep 8, 2016 at 15:00
  • Ok, so what is the granularity of business events coming for change requests (commands) such as add address to customer? Is that a single event or is it an add address event followed by associate address?
    – Erik Eidt
    Sep 8, 2016 at 15:03
  • @ErikEidt First an address has to be created. Then, a user can associate it to a person.
    – Ludovic C
    Sep 8, 2016 at 15:05
  • How many transactions (commands + queries combined) are you either predicting to run or actually running an hour? Is it close to or even over a million?
    – Andy
    Sep 8, 2016 at 17:39

2 Answers 2

1

You are allowed to create your read model projections as necessary, and you can either read the current read model state to update it, or, you can set up a pipeline where you read & update internal read model state, and from that (re)create the read model projections.

In some sense, you are always updating your read model picture of the world based on the last picture of that world + the effect of applying the next event. So, reading the read model is natural.

However, any way you do it, you have to be sure to consume the events (apply the events from the event source/log to the read model) in (the proper) order and using enough transactional atomicity so that downstream consumers always see a complete view of the state, which is either 100% the old state before applying an event, or 100% the new state after applying an event.

The eventual consistency means that when incoming events cease for a short while, the read model pipeline catches up. However, it would be bad to expose via in the read model, an inconsistent state (a state that never was a true picture of the world) say in which, either some change is only partially applied to the read model projections, or the effects of a later event is shown before the effects of an earlier one; this even if the state eventually converges on a true picture of the world.


You are still allowed to read the read model during update of the read model from the events in the event log.

However, there are some proponents of event enrichment, in which you do what you're saying for option 1; see: http://www.lavinski.me/generating-read-models-with-event-sourcing/, https://seabites.wordpress.com/2013/06/09/event-enrichment/

I think I'd still see prefer to see this as a function of the read model in generating its denormalized projections rather than a domain function, since it is not really a business function per se, but rather one of creating the right view, and keeping it wholly in the read model alone will decouple your command and query more from a maintenance point of view.

2
  • But I'm not affecting multiple aggregates with one command. I'm asking if a event Handler can gather information from elsewhere than from the event it handles : eg. from the readModel -- to update the read model.
    – Ludovic C
    Sep 8, 2016 at 21:42
  • Ok, my bad there; I've delete the preface.
    – Erik Eidt
    Sep 8, 2016 at 21:52
0

What is the most recommended way of getting that information (Line1, Line2, Line3) for the associated Address in the AddressAssociated event Handler in order to populate the PersonAdresses read model table?

For your situation? Don't do it that way.

  • Register addresses in the Address table as before.
  • Associate addresses in a separate association table, which can be joined to the Address table on AddressId
  • Create PersonAddresses as a view that the client application can query.

But in a circumstance where that approach doesn't work (writing to two different persistence stores, for example), the answer is to keep track of the state that you need in the event handler itself -- analogous to joining the two tables "in memory", and writing out the representation that you want.

1
  • So, in the event Handler, to "track the state" could mean interrogating the read model ?
    – Ludovic C
    Sep 8, 2016 at 21:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.