Łukasz Ostrowski
Łukasz Ostrowski

Reputation: 1020

Should I put command bus between controller and domain service?

I am working on a backend and try to implement CQRS patterns. I'm pretty clear about events, but sometimes struggle with commands.

I've seen that commands are requested by users, or example ChangePasswordCommand. However in implementation level user is just calling an endpoint, handled by some controller.

I can inject an UserService to my controller, which will handle domain logic and this is how basic tutorials do (I use Nest.js). However I feel that maybe this is where I should use command - so should I execute command ChangePasswordCommand in my controller and then domain module will handle it?

Important thing is that I need return value from the command, which is not a problem from implementation perspective, but it doesn't look good in terms of CQRS - I should ADD and GET at the same time.

Or maybe the last option is to execute the command in controller and then emit an event (PasswordChangedEvent) in command handler. Next, wait till event comes back and return the value in controller.

This last option seems quite good to me, but I have problems with clear implementation inside request lifecycle.

I base on https://docs.nestjs.com/recipes/cqrs

Upvotes: 2

Views: 2531

Answers (2)

Arnold Schrijver
Arnold Schrijver

Reputation: 3753

While the answer by @cperson is technically correct, I would like to add a few nuances to it.

First something that may not be clear from the answer description where it advises to "emit an event (PasswordChangedEvent) in command handler". This is what I would prefer as well, but watch out:

  • The Command is part of the infrastructure layer, and the Event is part of the domain.
  • So from the command you should trigger code on the AggregateRoot that emits the event.
  • This can be done with mergeObjectContext or eventBus.publish (see the NestJS docs).
  • Events can be applied from other domain objects, but the aggregate usually is the emitter (upon commit).

The other point I wanted to address is that an event-sourced architecture is assumed, i.e. applying CQRS/ES. While CQRS is often used in combination with Event Sourcing there is nothing that prescribes doing so. Event Sourcing can give additional advantages, but also comes with significant added complexity. You should carefully weigh the pros and cons of having ES.

In many cases you do not need Event Sourcing. Having just CQRS already gives you a lot of benefits, such as having your domain / bounded contexts well-contained. Separation between reads and writes, single-responsibility commands + queries (more SOLID in general), cleaner architecture, etc. On a higher level it is easier to shift focus from 'how do I implement this (CRUD-wise)?', to 'how do these user requirements fit in the domain model?'.

Without ES you can have a single relational database and e.g. persist using TypeORM. You can persist events, but it is not needed. In many scenario's you can avoid the eventual consistency where clients need to subscribe to events (maybe you just use them to drive saga's and update read-side views/projections).

You can always start with just CQRS and add Event Sourcing later, when the need arises.

Upvotes: 4

CPerson
CPerson

Reputation: 1222

As your architecture evolves, you may find that you require a command bus if you are using Processes/Sagas to manage workflows and inter-aggregate communication. If and when that is the case, it will naturally make sense to use that bus for all commands.

The following is the method I would prefer:

execute the command in controller and then emit an event (PasswordChangedEvent) in command handler. Next, wait till event comes back and return the value in controller.

As for implementation details, in .NET, we use a SignalR websockets service that will read the event bus (where all events are published) and will forward events to clients that have subscribed to them.

In this case, the workflow would be:

  1. The user posts to the controller.
  2. The controller appends the command to the command bus.
  3. The controller returns an ID identifying the command.
  4. The client (browser client) subscribes to events relating to this command.
  5. The command is received by the domain service and handled. An event is emitted to the event store.
  6. The event is published to the event bus.
  7. The event listener subscription service receives the event, finds the subscription, and sends the event to the client.
  8. The client receives the event and notifies the user.

Upvotes: 3

Related Questions