Book Image

Hands-On Microservices with Kotlin

By : Juan Antonio Medina Iglesias
Book Image

Hands-On Microservices with Kotlin

By: Juan Antonio Medina Iglesias

Overview of this book

With Google's inclusion of first-class support for Kotlin in their Android ecosystem, Kotlin's future as a mainstream language is assured. Microservices help design scalable, easy-to-maintain web applications; Kotlin allows us to take advantage of modern idioms to simplify our development and create high-quality services. With 100% interoperability with the JVM, Kotlin makes working with existing Java code easier. Well-known Java systems such as Spring, Jackson, and Reactor have included Kotlin modules to exploit its language features. This book guides the reader in designing and implementing services, and producing production-ready, testable, lean code that's shorter and simpler than a traditional Java implementation. Reap the benefits of using the reactive paradigm and take advantage of non-blocking techniques to take your services to the next level in terms of industry standards. You will consume NoSQL databases reactively to allow you to create high-throughput microservices. Create cloud-native microservices that can run on a wide range of cloud providers, and monitor them. You will create Docker containers for your microservices and scale them. Finally, you will deploy your microservices in OpenShift Online.
Table of Contents (14 chapters)

Domain-Driven Design

Using Domain-Driven Design (DDD) in our microservices will help us meet our microservices principles, but DDD will also help organize our products teams in ways that will benefit the value that we give from this type of architecture. But first, we need to understand what DDD actually is, and how we could apply it to create a clean architecture.

What is Domain-Driven Design

Domain-Driven Design is a software development approach to connect to an evolving complex model bounding into a core domain.

The term, Domain-Driven Design, was created by Eric Evans in his book with the same title.

When we approach a complex system, we usually abstract it to a model that describes the different selected aspects of the system, and how we could use it to solve problems. When multiple models are in play, and the code base of different models is combined, the software becomes buggy, unreliable, and difficult to understand. It is often unclear in what context a model should not be applied. The domain is the sphere of knowledge that the users of our system understand, and what they use to interact with our software; they are the domain experts.

In DDD, we define the context within which a model applies; explicitly set boundaries in terms of team organization, usage within specific parts of the application, and physical manifestations such as code bases and database schemas, keeping the model strictly consistent within these bounds.

Ubiquitous language

In DDD, we should build a common and rigorous language between developers and users. This language should be based on the domain model and will help us have a ubiquitous and fluid conversation with the domain experts, and will prove to be essential when approaching testing.

Since our domain model is part of our software, we should be precise to avoid any ambiguity and evolve both model and language as our knowledge as the domain grows. But when creating software, the usage of the ubiquitous language should not be only in our domain model, but also in our domain logic and even architecture. It will allow a ubiquitous understanding by any team member.

Creating tests that use the domain language help any team member to understand our domain logic.

Bounded context

When a domain model grows, it becomes complicated to have a unified domain model. Sometimes, we face a situation when we see two different representations of a concept, for example, let's examine the concept of family in a large model.

In a shopping platform, we may have the concept of products families, for example, our fabulous 32" LCD screen and the classical 24" CRT screen are part of the screen family. On the other hand, our speed offers and last day offers are part of our limited timed-offer family.

We could see that family may not be exactly the same thing on products and offers, probably they both have a unique name on their model, but in each context they may have a totally different model and logic.

In DDD, we separated them in to bounded contexts, a boundary that surrounds a model. This keeps the knowledge inside the boundary consistent, ignoring the outside world so we could still have our ubiquitous language for our domain model.

Context mapping

In a large application designed for several bounded contexts, we can lose sight of the global view. It is inevitable that the various bounded contexts will need to share or communicate data between each other. A context map is a global view of the system as a whole, showing how our bounded contexts should communicate with each other.

Context map example

This is an oversimplified example that shows three bounded contexts and how they are mapped. In the product context, we have our product and the family that it belongs to. Here, we will have all the operations for this domain context in it and it does not have a direct relation dependency to any other context.

Our offers bounded context has a dependency on the product domain context, but this is a weak relation that should purely reflect the ID of the product that a particular offer belongs to. This context will define the operations that contain the domain logic for this context.

In our shopping bound context, we have a weak relation with the product that belongs to a shopping list and will have the operations for this context. Finally, both offers and shopping concept have a relation with the customer that probably belongs to a separated bounding context.

Using DDD in microservices

Now that we have a clearer understanding of what DDD is, we need to review how we are going to apply it to our microservices. We could start with the following points:

  • Bounded Context: We should never create a microservice that includes more than one bounded context: it is better if we can map that whole context to a single microservice, something that indicates that our context is really bounded
  • Ubiquitous Language: We need to ensure that the language that our microservice speaks with is ubiquitous, so the operations and interfaces that are exposed are expressed in the context domain language
  • Context Model: The model that our microservice uses should be defined within the bounded context and use the ubiquitous language, even for entities that are not exposed in any of the interfaces that the microservices provide
  • Context Mapping: Finally, we need to review the context mapping of the whole system to understand the dependencies and coupling of our microservices

After reviewing these points, we will notice that we are in fact fulfilling the main principles defined before. Our microservices are modelled around business capabilities, our context domains, are loosely coupled as our context mapping shows, and have a single responsibility as a bound context should. Microservices that implement a bounded context could easily hide their implementation, and they will be nature isolated, so we could deploy them independently. Having those principles in place will make it easy to build for failure, having scalability and automation. Finally, having a microservice architecture that follows DDD will deliver a clean architecture that any team member could understand.


The ubiquitous language of a well-designed bounded context will make many tasks easy in a microservice life cycle, from working with the domain experts to tests or any tasks for the ops function of our team.