2
votes

Let's say i have an application structured around the basics of CQRS event sourcing:

  1. App dispatch Commands DTOs that are handled by Command Handlers
  2. Aggregate raise events when Command's datas are passed and applied
  3. Event Handler apply domain logic and side effects when they receive events they are interested to (in my case: they do projections).

My question is: in the steps 1 and 3, do we need to give all informations in Command and Event DTOs, or can we pass ID of some entities that handlers can then fetch from database ?

Example:

  • I have a Employee Aggregate
  • A RegisterEmployee Command
  • A EmployeeRegistered Event

Employee Aggregate has firstname, lastname, email, and some VO's like Address, Telephone etc.

It is the root of Worker Entity, so somewhere in my app i have a mapping of Employee Aggregate Id (UUID, domain generated) and Worker Entity Id (integer, database generated)

Command side:

On RegisterEmployee command, do i need to pass all the data needed to hydrate all Employee's fields ?

Then i would have a very big constructor with firstname, lastname, email, address, telephone1, telephone2 etc etc.

Cannot i give only basic fields in the command (firstname, lastname, email) and pass the Worker Entity ID, so that the RegisterEmployee Command handler can retrieve in the database the telephone and adress fields to pass to aggregate ?

Event side:

On the Event side, if my EmployeeRegistered Event Handler has to project a readmodel of my employee, does it need to have all the informations in the event itself to build the readmodel ?

Or can i put in the EmployeeRegistered event's payload only the basic info (firstname, lastname, email) and Worker Entity ID, so that the projection scripts does itself some join in the database to retrieve some complex and hidden informations ?

[EDIT]

Maybe the RegisterEmployee tries to do too many stuffs, and i should:

  1. dispatch a simple RegisterEmployee command with basics stuffs
  2. dispatch some other commands like AddEmployeeTelephone, AddEmployeeAdrress, etc

But in that case, doesn't it break a principle that a Registration action should occur in the same transaction ? what if my RegisterEmployee successes and the others don't ? i would end up with an incomplete Employee Registration process ?

[EDIT 2 ]

Hi Bola, you pointed it right. My context occurs in a migration from a legacy application to a cqrs one (at least some parts).

So as i want to let the legacy app do its own stuff, i only listens to persistence events on the db side and from them i dispatch domain commands.

That is why i can imagine having a command holding the "just persisted" entity id, in order to not duplicate the fields of the entity in the command, and to lighten the plumbing.

1
Can you clarify this: Cannot i give only basic fields in the command (firstname, lastname, email) and pass the Worker Entity ID, so that the RegisterEmployee Command handler can retrieve in the database the telephone and adress fields to pass to aggregate ? - Assuming that you are registering Employee for the first time( lets say from some kind of form) all the data regarding the employee should come as form data no? From which database you would fetch the data ?Bola
Good comment, some informations were missing. i edited my question.xefiji

1 Answers

2
votes

Something's off in the domain model/design. Let me take a step back and outline the ideal way so that you can map the missing pieces.

  • RegisterEmployee Command contains all data input for Employee Aggregate
  • Employee Application Service receives the command from Frontend
  • Employee Application Service initializes Employee Aggregate, preferably with the help of a factory method
  • Employee Aggregate raises EmployeeRegistered Event on successful validation and construction of a new Employee object
  • Employee Application Service persists the new Employee object with the help of Employee Repository
  • EmployeeRegistered Event is dispatched to the message broker after successful persistence transaction
  • EmployeeRegistered Event contains the entire dataset required by a subscriber
  • Subscribers for this Event may be in the same Bounded Context (BC) or in a different BC
  • Interested Subscribers catch the event and call the relevant Application Service in their own BC
  • Application services initialize infrastructure services and perform transactions (in your case, these are projections)

There should be no domain logic in Event handlers.