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)


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.

comments powered by Disqus