Observing multiple read models in wolkenkit

tl;dr: The wolkenkit client SDK makes it easy to read and observe multiple read models. How to update the UI accordingly depends on the UI framework you decide to use. In the simplest case, a single function call is sufficient.

When creating a client application for a wolkenkit backend, there are three ways to interact with the backend: First, you can send commands. Second, you can subscribe to events. And third, you can read (and optionally observe) read models.

Implementing a custom render function

When you have a look at the index.js file of the chat example, you can see that it contains a render function which is responsible for updating the UI:

render (messages) {
  const html = messages.map(message =>
    `<li class="message">
      <div class="label">${message.text}</div>
      <div class="likes" data-message-id="${message.id}">
        <span class="button">👍</span>
        <span class="count">${message.likes || 0}</span>
      </div>
    </li>`
  ).join('');

  view.messages.innerHTML = html;
}

This function does what in a real-world application you would typically do using a UI framework such as React or Angular. The reason why the chat example does not use a UI framework is because we wanted to keep the sample code simple and independent.

Fortunately, although the render function is probably one of the first things to catch your eye in the client-side code, it is not that important. Actually, you can pretty much ignore it and just think of it as a very simple custom solution for updating the UI.

Reading and observing a read model

The way more interesting part is a few lines below where you read and observe the read model that contains the sent messages:

chat.lists.messages.readAndObserve({
  orderBy: { timestamp: 'descending' },
  take: 50
}).
  failed(err => console.error(err)).
  started(view.render).
  updated(view.render);

This code makes use of the readAndObserve function that is provided by the wolkenkit client SDK. As you can see the function call takes a number of options, e.g. to specify the sort criteria and to limit the number of messages being returned. Additionally, there are a number of callbacks.

While the failed callback's intent is to catch errors, the started and updated callbacks are where the magic happens. Both of them take a function as a parameter, that gets called when the client starts reading the read model and when the read model gets updated, respectively.

Hence, both of them provide the result and a function called cancel if you want to stop observing the read model. A simplified form of the previous call may look like this:

chat.lists.messages.readAndObserve().
  started((messages, cancel) => {
    // ...
  }).
  updated((messages, cancel) => {
    // ...
  });

Updating the UI

Now, depending on the UI framework you use it's up to you to decide how to update the UI. In the chat example, since there is only a single list, we decided to implement a simple render function on our own.

For example, if you were using React, you would need to update the application's state as a result of one of the callbacks being called, and this way instruct React to re-render your application.

Reading and observing multiple read models

Finally, this is exactly what you need to do if you want to read and observe multiple read models. Regarding the wolkenkit client SDK it comes down to having two calls to readAndObserve instead of a single one. Obviously, the more interesting question is what to do with the results.

If you want to go with a custom render function as the chat example, you may want to provide two of these functions, each updating a specific part of the UI. If instead you use a UI framework such as React, you would need to update the application's state accordingly, and let React re-render your views.

But, before you do this, you should first take a step back and think about whether you really need multiple read models. One of the ideas of CQRS, which is a fundamental concept in wolkenkit, is that the read model is a prepared and denormalized view that contains exactly the data you need for a specific view in the application.

That means that if you need to show data from two read models, they should probably rather be a single one. And if you do this, you do not only follow CQRS more closely, it also frees you from the problem of havint do deal with multiple read models at once.

Twitter Facebook LinkedIn

Golo Roden

Founder, CTO, and managing partner

Since we want to deliver elegant yet simple solutions of high quality for complex problems, we care about details with love: things are done when they are done, and we give them the time they need to mature.