In this article, you'll learn how concepts from DDD are reflected in Kalix
One of the advantages of Kalix is that because it is a reactive microservice architecture, it goes hand in hand with Domain Driven Design (DDD). And not only the architecture but also the entities you work with in Kalix are aligned with DDD objects. What's also special about Kalix is that you only need to model your domain, not the database. Kalix abstracts that out for you, so you can focus on your domain and forget about how it's all stored. In one fell swoop, you've got a whole bunch of problems out of the way. In this blog, you'll learn how some of the basic ideas of DDD are used in Kalix.
TL;DR
You have the following relation between Kalix components and DDD semantics.
Kalix | DDD |
---|---|
Microservice | Bounded Context with a Ubiquitous Language |
Event Sourced Entity | Commands and Events |
Value Entity | Commands |
View | Query |
Protobuf service definition | Aggregates, entities and value objects |
Bounded Context and Ubiquitous Language
In DDD the idea of Bounded Context is about isolating certain parts of your business so you don't contaminate one context with similar ideas from another. Let's say you have a company with different departments. Each of them solves a particular problem of the whole. You could say that the business of the whole company is its *domain* and each department is a *subdomain*. From this perspective, each department has a particular context, which DDD calls a "bounded context".
This idea is complemented by the Ubiquitous Language, which suggests that you should refer to each entity or process in your business with a unique reference, a unique word. For example, at Walmart, there are Orders in the warehouse to replenish inventory, but that's very different from a purchase order from a customer. Order has a different meaning when it comes to replenishing inventory than when you get a request from a customer. Attempting to use a generic Order for both use cases is confusing to model and use. To make the meaning of order clear and unambiguous, DDD suggests that each term should be bounded to a context. These contextualized terms constitute the Ubiquitous Language.
In Kalix, you define the terms bounded to a context. The Kalix Service is the context. When you define the proto files in a service, you create the dictionary only for that service. The order defined in the service for stock replenishment is simply different from the order defined in the service for customer purchases. Each order is defined in its own service.
Kalix takes all the infrastructure issues out of the equation and puts you in a microservice architecture, so you can focus on the business domain and bridge the gap between developers and business experts. One of the biggest challenges in a project is finding the right boundaries and the right language. You need many iterations to perfect your model. An architecture that encourages you to work with a bounded context helps you do that.
Actions and Objects
Now let's move on to DDD actions and objects. Actions are commands, events and queries. Objects, on the other hand, are value objects, entities, aggregates and aggregate roots.
Command and Events are the opposite of each other.
A command is a request that can be fulfilled, but does not have to be. For example, with a reservation in a popular hotel. Let's say you want to reserve a room that you saw in your app, but when the command reaches the service, it queries the room availability and finds that there is no room available. So the room can't be reserved and you get a response that the reservation can't be created. You sent a command and in this case it was not fulfilled.
On the other hand, an event, what happened, is a fact. Suppose you requested the reservation, it was successful, and that creates a ReservationCreated event. Now you can save that event and have a whole range of possibilities with it afterwards. It is a great advantage to save the events. You can replay the history, similar to a DB where the logs are your backup. If the database gets corrupted, you have the logs with all the creations, deletions and updates. This is the same and is useful not only for backups. With this history you can replay the events if you found a bug or want to add a new feature to the entity. This way, reprocessing the same events can put the entity in a different state than before the error or the new feature. This is all about Event Sourcing. A topic on its own that we will be discussing in a blog coming soon.
In Kalix, you have these semantics out of the box with Event Sourced Entities. They receive commands and generate events.
Another useful aspect of storing these events is that you can query them. For example, you can query all reservations for a particular month and group them to calculate the company's profit. In Kalix, you can define such queries with a View. Like a materialized view in a DB, they update their state when new events are generated. A view does not affect the performance of processing commands or events. They are independent. An entity processes commands and events mainly in memory, it has to append to the DB which is an operation for which DBs are generally optimized. A view, on the other hand, reads from a DB. If this reminds you of Command Query Responsibility Segregation (CQRS), you're right. Command and Events are responsible for the write side of your application, while Query, the view, is only responsible for the read side.
Finally, let's take a look at DDD objects, i.e. value objects, entities, and their aggregates.
An entity is an object that you can refer to with an ID. It has some attributes that can change, but not its ID. This is its unique reference. A customer that changes its address changes some of its attributes, but not its customer ID. A value object, on the other hand, is an object that has attributes but no ID. You can model the address as such. Two value objects are identical if they have the same attributes. They are also often used to send messages, which can of course be commands or events.
In Kalix, you have entities like Event Sourced Entities. There are also Value Entities which do not track events. They are simpler. Their attributes are updated in place, not like ES entities. They represent a key-value store. So both Kalix entities are equivalent to a DDD entity. And in case you’re wondering, Kalix Value entities are not equivalent to DDD value objects.
The protobuf definitions of a Kalix service are suitable for both DDD objects. Let’s focus on the DDD value object. Usually you create one file for the service to define not only the RPC calls, but also their messages. The requests and responses are value entities. This file refers to the service API and usually has a name like my-service-api.proto. To represent the business, you keep another protobuf that represents the domain—more precisely, the subdomain—and has a name like my-service-domain.proto.
These two types together can form aggregates. An aggregate is a group of objects—entities or values—that together form a compounded entity. It can be a customer with an address and a few credit cards. These aggregates on top of being a compound are also changed us such, you could say transactionally. Let's say you have a customer service with the customer aggregate with the address and the credit cards. These credit cards or this address should only be changed through customer service. You should have no other way to change any part of this aggregate. For example, by updating it directly in the DB where these values are stored. In DDD, this entry point—the customer—is called the "Aggregate Root". The object you need to interact with to change something within the aggregate. And here you have to do that through the client's service.
You can easily create your aggregates with Kalix. Whether they are Event Sourced Entities or Value Entities. With the .proto definitions you can mix and match DDD entities and value objects to create the aggregates that fit your business.
As you can see, Kalix is inspired by the key ideas of DDD and many other ideas that are common in our industry, such as CQRS or Event Sourcing. You can forget about infrastructure and focus on your domain using basic DDD concepts like Bounded Context, Actions, and Objects. And take advantage of having a write side and a read side from the start to meet the performance needs of your service. Whether you're interacting with your entities or consuming their views.
If you want to know more, we have lots of documentation and can organize a demo for you on request.