CQRS and eventsourcing with API Platform III
February 16, 2019
Here comes Part III of the tutorial.
Part I was about Setup the API Platform and CLI tools, in Part II we have implemented user management and JWT-Authentication.
This chapter give you a short overview about the domain and implements the main model-objects.
All changes to part 2 you can find here.
Part III: Introducing and Implementing the Domain
I have decided to choose a synthetic example domain with only a small number interactions. The domain is a book library, an example which is easy to explain and does not have to contain too much domain logic. I my researches I found an implementation in .NET Core which gave me some domain input for this tutorial.
The Domain: a Book Library
Lets assume we are modelling a book library with 3 different services:
- a user service
- a book inventory service
- a book rental service
User Service
- we know two different user-roles, the customer (ROLE_USER) and the Admin user (ROLE_ADMIN)
- the Admin user can create/update/delete customers (username, password, first name, last name)
- if a customer has unreturned books, the admin can’t remove it in the system
Book Inventory Service
- Admin user can add/edit/delete books
- Books should contain the following informations
- ISBN(Required, Unique)
- Book Name(Required)
- Description
- Total number of books in the inventory (count)
- Number of books rented
- At least one book has to stay stock
Book Rental Service
- If one book inventory has been rented by one customer, it can’t be rented by others.
- One customer can rent 3 books at most.
The implementation
Each service stores his information in a separate table which is represented by a Doctrine entity class. The classes are located in src/Model, their mapping informations are defined by annotations. For this I have adapted the mapping information in config/packages/doctrine.
User Service
Is represented by the src/model/User.php. I have added a userprofile property to store firstname and lastname directly to the user class. For a real world implementation you should use a separate entity, for example a UserProfile class with a OneToOne-Relation.
Book Inventory Service
This service acts as a list of books based on their ISBN as the unique identifier. The inventory item src/model/BookInventory.php contains information about the book, the total number of book aquiered by the library and the number of books from them rented. Next to getters and setters I have added some useful methods:
- addBooksToInventory (admin-action for purchased book)
- removeBooksFromInventory (admin-action for book removal)
- rentBook (customer-action renting a book)
- returnBook (customer-action returning a book)
Book Rental Service
This service stores information about all rentals of books as a log. One book rental item src/model/BookRental.php contains the userId, bookId (ISBN) and date/time of rental and return. I have added one useful method:
- returnBook (customer returned the book)
Tests
The tests with database-interaction are running now with a sqlite-db. This i have specified in config/packages/test/doctrine.yaml.
Before running the tests, the database will be cleared. For this, I hace changed phpunit.xml.dist, adding another bootstrap.php, which executes some commands before running the testsuite.
In tests/Model are now three more testFiles, which are testing main domainlogic and database-interaction.
Whats next
In the next part we will implement the CQRS-pattern.