07 May 2019
CQRS and Event Sourcing implementation in PHP (2/4)
In the first part of the article about CQRS and Event Sourcing implementation in PHP, I’ve focused on explaining what are CQRS and Event Sourcing. I’ve mentioned the general idea of CQRS, Event Sourcing and Event Store. Also, I’ve presented their pros and cons. The second part of the article is about the implementation of the Write Model with Broadway library.
Let’s imagine that you need to implement a module to handle users in an application. They need to be able to log in and browse through the list of all the registered users.
According to the idea presented in the first part of the article, you divide the app into two parts. First will be responsible for saving and managing the users, the other will be delivering a simple list which includes some basic data and enabling logging in.
Step-by-step implementation of the Write Model
So, your task is to implement a functionality which allows adding users to the repository. Let’s check what we need:
- CreateUserCommand – a command to add a user,
- CreateUserHandler – a handler to process a command,
- UserWasCreatedEvent – an event which confirms a user creation,
- UserAggregateRoot – a user domain object,
- UserRepository – a repository to save an object.
The command is a class containing only the fields which are necessary to create a user. You can compare it to a traditional request you submit in the office. This kind of request needs to be filled and passed to be processed.
The handler is responsible for processing a request (CreateUserCommand). It’s a place where you can find the logic responsible for user creation.
Handler code looks like this:
Please note that the class inherits from SimpleCommandHandler provided by Broadway. Thanks to that, you don’t have to call it – Broadway will do it for you. The only thing you have to do is to provide a proper method name (format: ‘handle’ + command name).
See also: Separating business logic in PHP
To create a user object, you need to use the factory method. It’ll create a domain object basing on the provided parameters. Of course, you save only a hash for the password, so the password needs to be encoded before passing through. The encoder is injected as a dependency to the handler. The last thing you have to do is to save a user using a repository which also has been injected as a dependency.
An event code looks like the one on an example below.
The object above informs you that the user has been created. It also contains its details. Here, you have another Broadway requirement – implementation of serialization/deserialization of an event. You have to do it on your own. The rest (save/read of the Event Store event) is done by Broadway.
User Aggregate Root
Aggregate Root objects have to extend a class EventSourcedAggregateRoot.Thanks to that you get the possibility to apply events and to reconstitute objects from database.
Moreover, these objects have to meet a few additional requirements. First of all – you have to make sure that your object has its identifier. That’s why you have to implement getAggregateRootId() method.
Another requirement is to implement methods which are applying the proper event. Again, you have to provide a proper method name (format: ‘handle’ + event name).
Please note that in a constructor, you can’t set any properties. You have to create a proper event, then it’ll be processed through the method applyUserWasCreatedEvent(). This solution enables applying the events to the object whilst recreating it from the Event Store. In the listing below, I purposely skipped accessors for private properties because I didn’t want to complicate the presented code. But obviously, they are needed in the final solution.
To fetch and save the data in the Event Store, you have to create a proper repository (EventSourcingRepository) which you can use in your class.
EventSourcingRepository requires you to provide a few arguments:
- Event Store object,
- Event Bus object,
- a name of the class which will be operated (in the presented case it’s User),
- a class of the object factory; I chose NamedConstructorAggregateFactory, which means the use of User class factory method instantiateForReconstitution(); then all the events will be applied,
- eventStreamDecorators – an array of decorators which allow modifying events before saving them in the Event Store. You can add the metadata this way.
Additionally, I’ve created two methods to fetch the objects from the repository and save them in the same place.
It’s presented on the listing below.
This is how I’ve implemented the Write Model. Thanks to the use of Broadway, you don’t have to think about propagating the events. They will be put on Event Bus any time when a new event is saved by the repository. There they can be read by the read part of the app and used for the Read Model updates.
In the second part of CQRS and Event Sourcing implementation in PHP, I’ve focused on the implementation of the Write Model with Broadway library. The next part of the article will be about the implementation of the Read Model.