15 Boundary Microservices the Roles of Various Boundaries in Architectural Advancement

15 Boundary Microservices - The Roles of Various Boundaries in Architectural Advancement #

Hello, I’m Ouchuangxin.

In the previous lectures, we have already introduced that when designing microservices using DDD, we can use event storming to determine the boundaries of the domain model, define the boundaries of microservices, and establish the boundaries of business and system operations, thus ensuring the single responsibility of microservices and their ability to evolve with changing requirements.

When it comes to boundaries, the key points can be summarized as logical boundaries, physical boundaries, code boundaries, and so on.

So, what roles do these boundaries play in the evolutionary process of microservice architecture? And how should we understand these boundaries? These are the questions we will focus on solving today.

Progressive Architecture #

In the process of designing and implementing microservices, many people believe that “the key to microservices design is how many microservices the monolith is split into.” But is this really the case? In fact, it is not!

When Martin Fowler proposed microservices, he mentioned an important feature of microservices - progressive architecture. So what is progressive architecture? Progressive architecture is based on the first principle of supporting incremental and non-destructive changes, while supporting multidimensional changes at the application structure level.

So how can we determine if a microservices design is reasonable? It’s actually quite simple, just see if it meets this situation: as the business develops or the requirements change, in the process of continuously re-splitting or combining into new microservices, it does not significantly increase the cost of software development and maintenance, and the process of architectural evolution is very easy and simple.

This is also the focus of microservices design, which is to see if microservices design can support long-term and easy architectural evolution.

Microservices designed using the DDD (Domain-Driven Design) method can not only decouple the inside and outside of microservices through bounded contexts and aggregates but also easily achieve the recombination and updating of business functional building block modules, thereby achieving architectural evolution.

Microservices or Monolith? #

When some project teams split a centralized monolithic application into microservices, the first step is often not to establish a domain model, but to simply split the original monolithic application’s software package into several so-called “microservices” software packages based on business functions. However, the code inside these “microservices” still follows the centralized three-tier architecture pattern. The code inside the “microservices” is highly coupled, and the logical boundaries are unclear. Let’s temporarily call it “mini-monolith microservices”.

The following diagram also illustrates this process well.

img

As new requirements arise and the business grows, these mini-monolith microservices will gradually expand. One day, when you find that these expanded microservices need to be further split or some functions need to be restructured with other microservices, you will realize that the seemingly clear microservices have unknowingly transformed into bloated monoliths, and the code inside this monolith is still highly coupled and the boundaries are unclear.

“After years of hard work, I’m back to where I started overnight!” At this point, you will need to repeatedly go through the process of refactoring from a monolith to microservices. Think about it, isn’t this cost a bit too high?

In fact, the problem is already obvious, and that is boundaries.

These monolithic-style microservices only define one dimension of boundaries, which is the physical boundaries between microservices. Essentially, it is still a monolithic architecture pattern. When designing microservices, we should consider not only this boundary, but also define the logical boundaries and code boundaries within the microservices. Only then can we achieve the desired results.

Now you know that we must avoid designing microservices as mini-monolith microservices, but how exactly can we avoid it? Everyone wants clear boundaries, but how can we ensure them? DDD has already provided the answer.

The Role of Microservice Boundaries #

You may remember the concepts of bounded contexts and aggregates from the DDD (Domain-driven Design) methodology. They are used to define the domain model and microservice boundaries.

Let’s review the DDD design process.

During an event storming session, we identify user actions, events, and external dependencies in the business process. We use these elements to identify domain objects such as entities. Based on the business relationships between these objects, we group closely related entities together to form aggregates. The first layer of boundaries is formed between these aggregates. Then, based on business and semantic boundaries, we define one or more aggregates within a bounded context to form a domain model. The second layer of boundaries is formed between these bounded contexts.

For easier understanding, we categorize these boundaries as: logical boundaries, physical boundaries, and code boundaries.

Logical boundaries primarily define the boundaries between clusters of closely related objects within the same business domain or application. After relating and clustering different entity objects during an event storming session, we generate multiple aggregates and bounded contexts that together form the domain model of this domain. The boundaries between aggregates within a microservice are logical boundaries. Typically, a microservice will have more than one aggregate, and the code for different aggregates is isolated in separate aggregate code directories during development.

Logical boundaries are of great importance in microservice design and architectural evolution!

The architectural evolution of microservices is not arbitrary and needs to follow certain rules, which are defined by logical boundaries. During the evolution of a microservices architecture, we reorganize business capabilities on the business side based on aggregates and reorganize microservice code on the microservice side based on aggregate code directories. Because microservices designed according to the DDD methodology have clear logical boundaries, high business cohesion, and loose coupling between aggregates, we don’t need to spend too much time and effort when refactoring the domain model and microservice code.

Now let’s look at an example of a microservice. In the following diagram, we can see that the microservice contains the business logic of two aggregates. Each aggregate encapsulates different business capabilities, and the code for each aggregate is organized in separate aggregate directories.

As the business rapidly evolves, if a certain microservice encounters a performance challenge and needs to separate some business capabilities, we can easily achieve microservice decomposition by separating aggregates into new microservices based on aggregates.

img

In addition, we can also reorganize the functions and code of multiple aggregates within multiple microservices that have similar functionality, combining them into new aggregates and microservices, and making them independent as common microservices. Now, do you feel like you’re working on a platform?

Physical boundaries primarily define the boundaries between microservices from a deployment and runtime perspective. Different microservices are physically isolated in terms of deployment and run environment and run in separate processes. These boundaries are the physical boundaries between microservices.

Code boundaries primarily isolate different functional code within a microservice. During the development of a microservice, we establish corresponding code directories based on code models to achieve the isolation of different functional code. Due to the mapping relationship between the domain model and code model, code boundaries directly reflect business boundaries. Code boundaries can control the scope of code refactoring and avoid mutual influences between business and services. If a microservice needs to reorganize functionality, it only needs to be done based on aggregate code.

Proper understanding of the boundaries of microservices #

From the above content, we know that following the logical boundaries and code boundaries designed by DDD makes the evolution of microservices architecture less difficult.

The splitting of microservices can be based on domain models or aggregates, as aggregates are the smallest units that can be split into microservices. But do we always have to achieve consistency between logical and physical boundaries in the implementation? In other words, does the aggregate always have to be designed as a microservice? The answer is not necessarily, as this involves the issue of over-fragmentation of microservices.

Over-fragmentation of microservices will increase the maintenance costs of software, such as integration costs, deployment costs, operation and maintenance costs, as well as costs for monitoring and problem locating. In the early stages of project construction, if you do not have strong microservices management capabilities, it is not advisable to split microservices too finely. Once you have gained certain capabilities and the logical and code boundaries within the microservices are clear, you can split new microservices as needed and achieve the evolution of microservices architecture.

Of course, it is also important to remember that the service calls and data dependencies between microservices should comply with the design principles and development specifications of high cohesion and loose coupling. Otherwise, you will not be able to quickly complete the evolution of microservices architecture.

Summary #

Today we mainly discussed the role of various boundaries in the architecture evolution of microservices design.

Logical Boundary: The boundary between aggregated services within a microservice is a logical boundary. It is a virtual boundary that emphasizes business cohesion and can be converted into a physical boundary as needed, meaning that aggregations can also be separated into independent microservices.

Physical Boundary: The boundary between microservices is a physical boundary. It emphasizes the isolation of microservices’ deployment and operation, focusing on service invocation, fault tolerance, and runtime of microservices.

Code Boundary: The boundary between different layers or aggregations of code directories is a code boundary. It emphasizes the isolation between codes, facilitating the reorganization of code during architectural evolution.

With the above boundaries, we can achieve high business cohesion, loose coupling between codes, and clear boundaries. This enables fast implementation of microservices code splitting and composition, making microservice architecture evolution easier. However, one thing must be noted: a microservice with clear boundaries is not simply an evolution from a monolithic system to smaller monolithic systems.