DDD, Event store, UI

2019-05-10 07:33发布

问题:

I have a project which is designed or at least should be according to the well known DDD principles.

  1. Back - DDD + CQRS + Event Store

  2. UI - ngrx/store

I have a lot of questions to ask about it but for now I will stick to these two:

  1. How should the UI store be updated after a single Command/Action is executed ?

a) subscribe to response.ok

b) listen to domain events

c) trigger a generic event holding the created/updated/removed object ?

  1. Is it a good idea to transfer the whole aggregate root dto with all its entities in each command / event or it is better to have more granular commands / events for ex.: with only a single property ?

回答1:

How should the UI store be updated after a single Command/Action is executed ?

The command methods from my Aggregates return void (respecting CQS); thus, the REST endpoints that receive the command requests respond only with something like OK, command is accepted. Then, it depends on how the command is processed inside the backend server:

  • if the command is processed synchronously then a simple OK, command is accepted is sufficient as the UI will refresh itself and the new data will be there;
  • if the command is processed asynchronously then things get more complicated and some kind of command ID should be returned, so a response like OK, command is accepted and it has the ID 1234-abcd-5678-efgh; please check later at this URI for command completion status

At the same time, you could listen to the domain events. I do this using Server sent events that are send from the backend to the UI; this is useful if the UI is web based as there could be more than one browser windows open and the data will be updated in the background for pages; that's nice, client is pleased.

About including some data from the read side in the command response: this is something that depends on your specific case; I avoid it because it implies reading when writing and this means I can't separate the write from the read on a higher level; I like to be able to scale independently the write from the read part. So, a response.ok is the cleanest solution. Also, it implies that the command/write endpoint makes some query assumptions about the caller; why should a command handler/command endpoint assume what data the caller needs? But there could be exceptions, for example if you want to reduce the number of request or if you use an API gateway that do also a READ after the command is send to the backend server.

Is it a good idea to transfer the whole aggregate root dto with all its entities in each command / event or it is better to have more granular commands / events for ex.: with only a single property ?

I never send the whole Aggregate when using CQRS; you have the read-models so each Aggregate has a different representation on each read-model. So, you should create a read-model for each UI component, in this way you keep&send only the data that is displayed on the UI and not some god-like object that contains anything that anybody would need to display anywhere.



回答2:

Commands basically fall into one of two categories : creation commands and the rest.

Creation commands

With creation commands, you often want to get back a handle to the thing you just created, otherwise you're left in the dark with no place to go to further manipulate it.

  • I believe that creation commands in CQS and CQRS can return an identifier or location of some sort : see my answer here. The identifier will probably be known by the command handler which can return it in its response. This maps well to 201 Created + Location header in REST.

  • You can also have the client generate the ID. In that case, see below.

All other commands

The client obviously has the address of the object. It can simply requery its location after it got an OK from the HTTP part. Optionally, you could poll the location until something indicates that the command was successful. It could be a resource version id, a status as Constantin pointed out, an Atom feed etc.

Also note that it might be simpler for the Command Handler to return the success status of the operation, it's debatable whether that really violates CQS or not (again, see answer above).



回答3:

Is it a good idea to transfer the whole aggregate root dto with all its entities in each command / event or it is better to have more granular commands / events for ex.: with only a single property ?

Indeed it is better to have granular commands and events. Commands and events should be immutable, expressive objects that clearly express an intent or past business event. This works best if the objects exactly contain the data that is about to change or was changed.