So, we had the idea of digitizing our whiteboards. In the previous part of our series we modelled our domain using an interdisciplinary approach with Domain-Driven Design. This time we’ll turn this model into a realtime API prototype.
But before we dive into some code, we should take a minute to learn about an important architectural aspect of wolkenkit: the separation between writing and reading state.
Writing means changing the state of our application and is accomplished by sending a „command“ to our backend. Reading actually means querying the state of our application. Both of these tasks have different needs and requirements.
When changing the state of our application, we want to ensure consistency as much as possible. When we need to read parts of our application’s state, we don’t want to query data from lots of different tables or collections in order to retrieve the data needed for a particular view or screen. Reading the application state should feel as fast and snappy as possible. So basically for every view of our application we should have a perfectly optimized read model to query from. This obviously means lots of duplicated data which is hard to update consistently. See the problem here?
So why not separate these two parts in order to optimize for each of them? Scaling can be handled independently on both sides and is a nice side effect that we gain from this separation. And it can happen according to the specific needs of our application.
Just like Domain Driven-Design, this pattern isn’t new; in fact, quite the opposite: it’s called CQRS – Command and Query Responsibility Segregation – and has been around for quite a while now.
Still we’re left with one important question: how do we keep these two sides in sync?
That’s where the events that we’ve modelled in our previous article come in handy. Until now we haven’t really taken advantage of them. But we can actually use the stream of events emitted from our write model to build up the read model. Think of the read model as an interpretation of the facts that happened inside our domain.
Looking back at the model we sketched collaboratively, we came up with two different aggregates: the board and the post. Each of them provides a set of commands (the blue post-its) and events (the orange ones).
In this case we define a command called mount that checks if the client has provided a title, otherwise it rejects the state change. If a title is given the mounted event is published and the data is passed on to it. Our board should later be reachable via a clean URL, that’s why we create a slug for the board and add it as an additional attribute to the event. Finally in the events block, we define how the state of our board will change when a particular event happens. In this case we react to the mounted event and set the title and the slug.
We are now able to change the state of our application. But how how do we read it, or in other words, how do we display a list of our boards? Let’s move on to the read model.
A read model is just an event listener that is able to subscribe to and act upon the stream of domain events. In this case it only listens for mounted events published by our board aggregate. If this event happens, it adds an entry to the list. Again, wolkenkit will instantly notifiy and update all clients subscribed to this list, so we can focus on transforming the events instead of wrestling with the details of the Websocket implementation.
We’re now be able to write and query the state of our application. Seems like the perfect time to try it out – so let’s go ahead and start our app. To do so, we need the wolkenkit comand line interface (CLI) and Docker for Mac. Once this is set up, we run wolkenkit start and let the CLI take care of the boring details: it packages our application’s source code into lightweight containers and runs them alongside with a couple of infrastructure containers (e.g. database and message queue) inside of our local Docker server.
So now the app is running locally and is serving an HTTPS and Websocket-API. But how do we quickly verify that it is working correctly?
One possible next step is to test the API we’ve just created which could already be done during a modelling session. However, building a UI that works across different devices is often one of the most challenging tasks of software projects. So we can use a little helper called console.wolkenkit.io to connect to wolkenkit apps, which in our case is running locally on our dev machine.
Just like any other client that uses the wolkenkit SDK it will grab the configuration from the backend and build up a client side API that reflects the model we’ve just defined on the server side. You can use it to issue commands, track the resulting events and read and observe the state of our boards list.