The flow of simple CRUD (Create, Read, Update and Delete) applications can be described using the following steps:
- The controllers layer handles HTTP requests and delegates tasks to the services layer.
- The services layer is where most of the business logic lives.
- Services use repositories / DAOs to change / persist entities.
- Entities act as containers for the values, with setters and getters.
In most cases, for small and medium-sized applications, this pattern is sufficient. However, when our requirements become more complex, the CQRS model may be more appropriate and scalable. To facilitate that model, Nest provides a lightweight CQRS module. This chapter describes how to use it.
First install the required package:
In this model, each action is called a Command. When a command is dispatched, the application reacts to it. Commands can be dispatched from the services layer, or directly from controllers/gateways. Commands are consumed by Command Handlers.
Here's a sample service that dispatches
KillDragonCommand. Let's see how the command looks:
CommandBus is a stream of commands. It delegates commands to the equivalent handlers. Each command must have a corresponding Command Handler:
With this approach, every application state change is driven by the occurrence of a Command. The logic is encapsulated in handlers. With this approach, we can simply add behavior like logging or persisting commands in the database (e.g., for diagnostics purposes).
Command handlers neatly encapsulate logic. While beneficial, the application structure is still not flexible enough, not reactive. To remedy this, we also introduce events.
Events are asynchronous. They are dispatched either by models or directly using
EventBus. In order to dispatch events, models have to extend the
apply() method does not dispatch events yet because there's no relationship between the model and the
EventPublisher class. How do we associate the model and the publisher? By using a publisher
mergeObjectContext() method inside our command handler.
Now everything works as expected. Notice that we need to
commit() events since they're not being dispatched immediately. Obviously, an object doesn't have to exist up front. We can easily merge type context as well:
Now the model has the ability to publish events. Additionally, we can emit events manually using
info Hint The
EventBusis an injectable class.
Each event can have multiple Event Handlers.
Now we can move the write logic into the event handlers.
This type of Event-Driven Architecture improves application reactiveness and scalability. Now, when we have events, we can simply react to them in various ways. Sagas are the final building block from an architectural point of view.
Sagas are an extremely powerful feature. A single saga may listen for 1..* events. Using the RxJS library, it can combine, merge, filter or apply other
RxJS operators on the event stream. Each saga returns an Observable which contains a command. This command is dispatched asynchronously.
info Hint The
ofTypeoperator is exported from the
We declared a rule - when any hero kills the dragon, the ancient item should be dropped. With this in place,
DropAncientItemCommand will be dispatched and processed by the appropriate handler.
CqrsModule can also be used for handling queries. The
QueryBus follows the same pattern as the
CommandsBus. Query handlers should implement the
IQueryHandler interface and be marked with the
Finally, let's look at how to set up the whole CQRS mechanism.
EventBus are Observables. This means that you can easily subscribe to the whole stream and enrich your application with Event Sourcing.
A working example is available here.