Implementing Lagom Readside Persistence Using Slick
Persistent Entities in Lagom hold state of individual entities. In other words, they cannot be used to serve queries that span more than single entity. This requires us to create another view of the data that is tailored to serve rather optimized queries that span over multiple entities, which brings us to the most talked about separation of the write-side and the read-side of the persistent data, also known as CQRS (Command Query Responsibility Segregation) pattern.
In this blog post I would like to demonstrate how read side persistence is implemented in Lagom using Slick which is a modern database query and access library for Scala that allows us to write database queries in Scala thus profiting from static checks, compile-time safety and compositionality of Scala.
Dependencies
Lagom's Slick support is build on top of it’s support for storing persistent entities in a relational database. This requires us to add lagomScaladslPersistenceJdbc
as our dependency. We would also require to add a JDBC database connector driver of our choice. We will be using mysql connector for this demo.
Notice that we don't require Slick
dependency, for it being natively supported by the framework itself.
Configurations
Lagom uses Play’s JDBC support to configure and create a connection pool, which requires us to provide details, such as, JDBC driver, database URL, etc in the application.conf
to help Lagom with the same. We also need to configure a correct Slick profile, which is slick.jdbc.MySQLProfile$
in our case. These profiles abstract relevant SQL dialect, data type encodings, or other idiosyncracies.
Application Loader
Next we need our application loader to load relevant read-side persistence components which includes ReadSideSlickPersistenceComponents
and HikariCPComponents
.
Note: Please be informed that the order in which the traits ReadSideJdbcPersistenceComponents
and WriteSideCassandraPersistenceComponents
are extended to create EmployeeApplication is of grave importance due to a bug in Lagom which requires trait ReadSideJdbcPersistenceComponents
to be mixed in before the trait WriteSideCassandraPersistenceComponents
.
Model Entities
Let's now model our employee entity as Slick table definition as shown below.
Query Readside
Lagom makes use of the database configurations from our application.conf
to create and inject an instance of Slick Database
required as a dependency in our repository.
Unlike get queries, createTable
, addEmployee
and updateEmployee
queries return Slick DBIOAction
as required by ReadSideProcessor.ReadSideHandler
that handles events generated by Persistent Entities, which I'll introduce later. It is the Slick’s Database
which facilitates the execution of these DBIOAction
.
Slick’s Database
also manages the execution of blocking JDBC calls in a thread pool designed to handle them, which is why db.run()
API returns a Future
.
Update Readside
Every state change in a Persistent Entity is persisted in Cassandra as logs of historical events, which is how Lagom implements what's called as Event Sourcing. It is in response of the persistence of these events that the read-side is required to update the read-side data model. Lagom has a built in support for populating the read-side view out of the box. This is achieved by implementing a ReadSideProcessor
with assistance from the SlickReadSide
support component.
The ReadSideProcessor
implementation must implement a buildHandler
that returns a ReadSideHandler
, which is used by Lagom to prepare read-side processing and eventually handle the events returned by aggregateTags
method definition. All events with a particular tag are consumed as a sequential, ordered stream of events. You can refer Lagom documentation to learn how tags are implemented.
Now the Lagom's built in support comes into play when you extend ReadSideSlickPersistenceComponents
trait in your application loader.
As you can see, besides bringing in Slick Database
and JdbcProfile
right from our application.conf
as we discussed above, the ReadSideSlickPersistenceComponents
trait also brings in an implementation of SlickReadSide
.
It is this SlickReadSide
implementation that implements a ReadSideHandler
builder out of the box for us. We can now implement our ReadSideProcessor
as shown below.
The event handlers processEmployeeAdded
and processEmployeeUpdated
eventually ask our repository implementations to return DBIOAction
in response to respective events that these handlers are registered against.
And this is how we implement read-side persistence in Lagom using Slick. You can find the complete working implementation here on github.