In the programming world, design patterns are very common. This is no different when it comes to web development. With the internet popularity and then after Web 2.0, many patterns developed for the web were widely disseminated with the intention of making the development more dynamic and simple for new features.
Patterns such as MVC (Model-View-Controller), HMVC (Hierarchical Model View Controller), and MTV (Model Template View), among others, inspired the creation of various frameworks such as Django, Ruby on Rails, Spring MVC, and CodeIgniter, for example.
All these frameworks are excellent for creating web applications quickly and without great concern for application architecture. This is because much of the work is done by the framework. All these patterns were thought to be only applied to a web application with all the required business rules. Typically, these applications, where all business rules are on the same code base, are called monoliths.
For years, the monoliths absolutely reigned in the web development ecosystem. Many companies looking for space in the market validated products by creating software on these full-stack frameworks. Many monolithic software applications went to the internet and, over time, a word has emerged as a problem for these monolithic applications: success.
- Maintenance on the same code base can be complicated due to merges that are difficult to apply
- Implementing new features is increasingly complex and may take longer than expected
- Application scalability
- Deploying new features without impacting what is already online is challenging
- Architectural changes are always very complex
These are just some examples of the kinds of problems that may exist in a monolithic application. These difficulties are a good motivation to migrate monolithic application architecture to microservices. Increasingly, adopting microservices has been the path taken by the software engineering industry and most companies, where the word success has meant trouble for the provision of practice and scalable business. The advantages of the microservices architecture are many:
- An exclusive business domain for each microservice, facilitating the implementation of new features
- Better definition of business without cyclic dependency between them
- Independent deployment
- Simplicity to identify errors
- Technological independence among microservices
- Independence between teams
- Implementation of isolation
- Possible scalability for specific microservice
Showing you how to make the transition from a monolithic application to microservices, applying appropriate patterns, and showing you possible implementation misconceptions, is the aim of this book. All modifications will be applied to the same project, a news portal that has interactivity, a recommendation system, authentication, and an authorization system. Throughout the book, you will be shown every step of this migration when applying design patterns, and both internal and external migrations when it comes to the communication layer between microservices. Of course, to get to that point, you first need to understand some important concepts to efficiently implement the microservices.
It is time to get to know the application that will be used throughout the book. This application will be the source of all explanations of concepts and all practical code that will be developed in the book. The base system is a news portal that consists of, basically, three areas:
- News: This is the news itself.
- Recommendations: These are responsible for storing user preferences and thus are able to offer specific news to users or even compose a completely unique home page according to the user profile.
- User: This is the basic registration information of a user.
All the application's business is on the same source code, that is, a monolithic software. The application was developed on the Django Framework, using PostgreSQL as a database, and Memcached as cache, which is only applied to the database layer.
With this structure, if there is an overload on the level of recommendations, all the applications must be scaled, and not only the part referring to the recommendations, because the application is monolithic. Another problem is that if one commits and incurs a problem, it propagates errors in segments that have no relationship with deficiency in the composition of domains. Something expensive for the application changes in the stack. If you want to change the type of cache used, then all other caches will be lost.
The OOP concepts can be applied to the design of microservices, not only in the internal design of the application but also in the architecture and the business division. When it comes to Domain-Driven Design (DDD), it is no different.
DDD came from the book Domain-Driven Design written by Eric Evans. The Evans book is a large catalog of patterns, coming from over 20 years of the author's experience developing software using OOP. It is very important to note that OOP is not only inheritance, interfaces, or anything else of the type. OOP's main ideas are as follows:
- Code alignment with the business
- Favoring of reuse
- Minimal coupling
Evans's book is divided into four parts:
- Putting the domain model to work
- Model building blocks-driven design
- Refactoring to deeply understand the model
- Strategic design
All the preceding parts can be applied to microservices. Model building blocks-driven design and refactoring to deeply understand the model internally applies to microservices, that is, the code itself. The others can be applied either internally in the software, but can also be used to design microservices and their signatures.
The first part is emphatic about the need to use ubiquitous language for communication between those responsible for the business, and the engineering team responsible for the development. This language consists of terms that are part of everyday conversations between business experts and development teams. Everyone should use the same terms in spoken language, the source code, and the signing of microservices. This means that when the business specialist says, The home page should seek breaking news with the title and description the ubiquitous language applied in the code will be represented as follows:
- Microservice news
- Endpoint recent news
- Payload with attribute title and description
This type of communication will mitigate errors in understanding requirements and maintain the general knowledge about application unison. Another important point is that, with the ubiquitous language, identifying areas is simpler because, as interactions have standardized terms, a word may indicate something new.
The fourth part will clarify the boundaries of a microservice and ease of management between the parties. Besides having a ubiquitous language used in the development of a consistent and adequate microservice, some strategies are necessary for dealing with complex systems, where multiple pieces of software (developed by several teams) interact. Delimit the context in which each team works and what is the degree of interaction between these teams and these contexts? Of the many tools that the DDD provides us, three are more prominent for efficient microservices:
- Context maps: These are the communication paths between microservices with appropriate interactions between microservices teams. After the analysis of the areas are already defined, the team can choose to be dependent on another team for domain language.
- Anti-corruption layer (ACL): This is the function that translates foreign concepts for an internal model to provide loose coupling between the domains.
- Interchange context: This provides an environment for both teams and discusses the meaning of each foreign term and translates the languages of microservices.
Thinking about the application we are working on, the DDD would be very useful for avoiding misunderstandings in the interpretation of how microservices should work. A common misconception in news systems is about the terms user and author; both are users of the system, one as a player and another as publisher, but if a product owner says, "The user published bad news" we have a problem in communication between product teams and development teams. This may result in an inconsistent microservice within the business itself. Another problem is that the phrase spoken earlier by the product owner suggests an unwanted feature, which is a user who can publish materials. DDD is just thinking about defining the microservice domain and standardizing terms to generate consistent internal models and an API with solid meaning. The difference in meanings or representations for the same attribute is known as a semantic gap, and is exactly what we are dealing with throughout the chapter.
Besides the aspects mentioned previously, helping to create microservices intact, there is a feature of DDD which undoubtedly is the most important when it comes to designing microservices. The concept of bounded contexts is essential to determine the range of a microservice and in the end, the responsibility that the microservice has. The most important thing is understanding that without these, coupling limits will be high and concepts such as single responsibility can never be achieved.
Another principle that is applied to microservices and comes from the OOP world is specifically the letter S in SOLID. What could be thought before the class level should now be through the application level, so that microservices can be really micro in terms of what really matters at the domain level.
The microservice domain cannot be large; on the contrary, it must be limited. The limits that were cited in DDD return to be applied now and with more intensity. Precisely, the limits in the microservice domain is what will make this application sensitive to changes and open to the perception of possible errors.
It is a very common difficulty in maintaining pure microservices in their domains. The natural tendency, either by habit or ignorance, is to try to group all the business rules or similar codes in the same microservice, without even understanding whether they are part of the same domain.
To illustrate, think of the application on which we are working, our news portal. News and recommendations virtually work together all the time. Recommendations always put together some news that has some of the related labels. At first, it makes sense, because as the recommendations are always related to the news, apparently this does not cause a problem and, moreover, could reduce problems such as network latency. The main concept could be represented by the following diagram:
However, creating a microservice containing news and recommendations will generate unnecessary and very expensive engagement for future changes. In a simple demonstration exercise, we can think of a number of business changes with which this engagement would generate problems.
A new business requirement arose. Those responsible for thinking about the product had an idea to use recommendations to compose a personal home page according to the news that stands out most in our portal. So, the recommendations aren't just connected to the news anymore, but to the user too. With this new requirement, some problems relating to the coupling done previously will emerge at the level of deployment, scalability, and maintenance features code.
Based on the new requirement mentioned previously, it is clear that news and recommendations work together, but they are totally different domains from each other. Identifying areas to apply the principle of sole responsibility for each microservice is crucial to application architecture. The following diagram shows the main idea of the microservice distribution:
Working together and in concert, Home requests information either from News or Recommendation. There are some forms of data orchestration that we will come to later in the book, but now it is important to understand that Home can consult separate services and compose the received data conveniently.
The separation of News and Recommendation results in an application where the deploy becomes a simpler, more consistent code base and the fully defined and specific business domain for a purpose.
The published interface is a term that usually generates a lot of confusion with the public interface. It is critical to understand the difference between the two terms: microservices and distributed source systems.
Think of a microservice. All internal microservice code will be used and shared among the development team; class methods are abstractions or attributes and can all be part of the public interface between teams. This is because of the convenience to notify and make changes in the event of possible refactoring. There is no point in generating a lot of bureaucracy at the development level, just for the features to gain speed in the implementation.
When it comes to the published interface, however, it is different. The published interface is what the microservice developers release. The published interface is what will be consumed by the internet. A good example is the Single Sign-On (SSO) API. Imagine that APIs suffer sudden changes to implement new features such as security and that these changes do not have a good system of alerts for all customers of these APIs. It is simply not appropriate to use this SSO service, because of updates, the API client suffers from incompatibilities.
Published interfaces should have more control and be more resilient to refactoring. Usually, they apply only to external application clients. The less possible changes in the level of the signatures, the better. The following diagram shows the possibility of maintaining the published interface signature:
- Published versioned interfaces: An efficient version control to indicate when something, deprecated is key. Not only that, but it will also indicate what the new version is and when the deprecated version will be deactivated permanently.
- Small published interfaces: A large payload is much more susceptible to change than a more specialized payload. Applying the concepts of DDD on these payloads is very healthy.
- Published external interfaces: Do not create the concept of published interfaces for internal development teams. This creates a slow process of change and implementation features.
It is common to think of the concept of public interface versus published interface as something similar to the public versus private OOP, but they are actually different. The published interface does not mean depriving the client of resources, but rather directing the customer to consume adequate resources resiliently.
Discussing this topic is very interesting and relevant to the microservices ecosystem. Deploy, upgrade, replace, and scale are great advantages that microservices have over monolithic systems, when it comes to most aspects of their functions.
A standard aspect of the professional software development world is version control. This is because developers are pretty much working on features and maintenance of legacy code in the same application, at the same time.
In the end, a landmark (tag) is created in the application and this landmark is sent to production; this process is called deployment. At that same point, some problems may arise.
Let's consider a situation; in our news portal, one developer is working on an important feature for recommendations while another developer is working on fixing bugs. Both commit to hitting the same target. At the time of deployment, there is a major problem. The bug in news was not fixed successfully, which prevented the new feature from going into production. Software can be thoroughly tested, and even though much attention is given to each task, the unforeseen can still happen.
When it comes to microservices, this kind of problem is reduced drastically. Rethink the same scenario.
On our news portal, one developer is working for a few weeks on an important feature for the
Recommendations microservice and another developer is working on a bug in the news microservice. Both commit to hitting the same target, each in their respective microservice; however, we still encounter a problem during deployment. The bug in news was not fixed successfully, which prevents the new version of the news microservice from going into production, but the
Recommendations microservice is perfect and the new feature goes into production without any problems.
This is perhaps one of the main positive points when it comes to independent deployment. Of course, the complexity of maintaining the operation of multiple machine instances generates more complexity, but if you think about it, in a world of cloud computing, the complexity of multiple instances would be the same even though the application was monolithic, as the need for scalability is always real.
Later in the book, we see some patterns of deployment; we just focus on reducing complexity and practicality to perform deploys continuously.
- Never share libraries between microservices: This means that each microservice has a stack that is totally independent of any other microservice. Sharing libraries is an error that generates high coupling and problems at the time of deployment. The microservices can start with the same stack, although it is usually best to analyze the domain and the data structure to see if the stack proposal is compatible or not. However, starting with the same stack does not mean keeping concurrent versioning. Another aspect that needs attention is to completely avoid creating business components on specific versions of a library stack. This approach prevents any technological developments in the microservice and means that, for example, security patches cannot be applied.
- Strong delimitation of microservice domains: We have already talked about bounded contexts, but it is worth reiterating again. The microservice limits are essential to determine whether the domain really is compatible with microservices architecture, or whether what is being designed is only a monolithic part decoupled from the rest. The loose coupling is what defines a microservice subject to upgrades and changes in the level of business without major conflicts with the ecosystem, in which the microservice is inserted.
- Establish a client-server relationship between microservices: This means that each microservice is a separate application and has complete autonomy over itself. When a microservice depends on another microservice's business resolutions, we have an alert point. The microservices can communicate with each other freely to ask for information, but never to solve business issues. When a microservice sends a message to another and is waiting for the answer to complete a task, there is an error. This error is critical and will result in scalability and transactional issues. When a microservice sends a message to another, there is a very strong idea there: asynchrony. As one microservice server performs tasks and provides information, another client microservice requests information. When the two faces—server and client—are intrinsically linked to a microservice, there is a design error.
- Deploy in separate containers: This approach not only facilitates the independent structure of a microservice, but also ensures that a fault in one microservice is totally individual, without disrupting an entire microservice pool. When we speak of separate containers, we are not necessarily talking about virtualization. The containers in question can be physical; it is a matter of the strategy and resources of a company, but the fact is that it is not healthy to keep more than one microservice in a container. It is important to remember that failures will occur, and when they occur, it is important to be prepared to mitigate the failures. Microservices as a group in a single container means that there will be a failure when a cyclomatic microservices burst occurs.
Separate containers are also essential for upgrading tools that are part of the stack, but that are not properly coded, such as databases and caches.
Scalability of speech is a common approach; see The Scale Cube which is discussed in the book The Art of Scalability from Martin L. Abbott and Michael T. Fisher. The concepts of the Scale Cube are fully applicable to microservices, and web applications in general, that need to be scalable:
The concept of a scale cube shows that there are basically three forms of scalability: x-axis, y-axis, and z-axis. To better understand each of these three approaches, we will use some diagrams.
The problem with this strategy is that resources such as databases and caches will be required, since the number of applications that accesses these features gradually increases, as necessary, to scale. For this strategy, caches require more memory and databases need a pool of greater connections, something that does not always result in a benefit:
This join between y-axis and x-axis allows us, occasionally, to bring scalability to just part of the microservices. In the following diagram, it can be seen that
News was the most scaled microservice, followed by
Users have no major changes. This type of scalability technique greatly reduces the drawbacks of shared resource access, as each microservice structure manages and uses only its own resources, such as caches and databases. Take a look at the following diagram:
The z-axis is very similar to the x-axis when it comes to scalability structure, as it distributes exactly the same code on each server. The big difference is that each server responds to a specific subset of data. In this strategy, the search is providing not only scalability regarding the application, but also the data you use.
The following diagram shows a little example:
This strategy is not entirely ruled out when it comes to microservices, but its use is a little different. The applicability ends up not being on the verbs, but on geolocation. This means that, in a global application, the database of a microservice is distributed by region and is preferably available for this region, that is, people who access the website in Europe will, preferably, see the European news.
The definition of how microservices are scaled is directly linked to business strategy. From a technical point of view, the focus is to provide a flexible software strategy allowing changes as they certainly occur.
Updates to microservices are normal, but sometimes these updates may compromise the health of a microservice. New features can cause the microservice to absorb many responsibilities that go beyond the original domain idea.
A common mistake is adding new features and invalidating old ones without removing them completely. Some features of the development processes become more clear when a new microservice is created that is intended to replace an old one.
This process may seem more time consuming, however, it is very healthy for the application as a whole. Rethink whether old features still make sense, remove any zombie code which has no more relevance to the business, becoming consumers of resources and aggregators of complexity.
The replace process, when it comes to microservices, is very simple, as shown in the following diagram:
The concept applied to the replacement process is very simple. With control as the balancing layer, which will direct 90% of the requests for the old microservice and 10% for the new microservice, it is possible to monitor and analyze how mature a new application is and if no feature has been forgotten or has unwanted side effects.
This approach reduces the error effect on production, and provides real data on the new application. As the new microservice gains maturity and confidence in the availability of features, a higher percentage of requests is released for the new microservice. Importantly, the microservices, due to the size of the small business scope and low coupling, are easily replaceable. A total replacement service is a natural process when it comes to evolution, both in business and as a stack.
In monolithic systems, many projects fail to be successful in the move to microservices architecture just because of problems in the communication layer. Of course, when we talk about containers, distributed applications, and business domain partitioning, some terms may amaze you—these terms are latency and data translation.
Communication in a monolithic application is made up of internal components, such as methods, functions, attributes, and parameters. In this ecosystem, latency and data translation are irrelevant. In the world of microservices, they are topics that must be thoroughly analyzed.
There are two methods of communication between microservices:
It is important to understand how each of these forms works. Let's see how:
Have a look at the preceding table; the type of communication adopted will vary according to the need of the domain. For direct and sequential systems, a synchronous communication approach may be more appropriate. In the case of tasks that do not need an immediate response, the asynchronous approach can be the most appropriate.
When it comes to the communication layer between microservices, synchronous is certainly the most widely used approach. Within this topic, some protocols are well known and others less so. The range of direct protocols is as follows:
Arguably, the most commonly implemented is HTTP. Many microservices use HTTP to communicate with each other, where as the HTTP is typically used with JSON.
The problem with this approach is that, with HTTP, JSON can generate an unwanted processing time to send and translate the information. Some teams that use JSON with HTTP only adopt the keep alive strategy for app-to-app communication and conventional connections to APIs.
When it comes to HTTP, API with JSON is practically normative. However, for internal communication between microservices, this is quite questionable. A good approach, in this case, considering problems of latency and data translation, is the use of binary traffic for communication between microservices.
There are some very interesting options for this approach: Avro, Protocol Buffer with CPRM, and Thrift are some examples. Another important point is that with binary we are not tied to any specific technology, and changing the communication interface with this technology is extremely simple.
In some direct communications between microservices, timing may be important, but there are other times where the process can simply be asynchronous; there's no need for an immediate response or confirmation of success, all that is required is to simply run a task. For this approach, the message broker is just perfect.
Some software applications appear a good choice for message brokers, such as RabbitMQ, ActiveMQ, ZeroMQ, Kafka, and Redis. Each of these options has its own peculiarities, some are faster, others are more resilient. Again, the business setting is going to determine which technology is used.
There is no single solution for everything in software development. It's a phrase I've heard a few times and it's the truth. A very interesting feature of a well-composed microservice is the possibility of a microservice with completely different technology from another microservice. This heterogeneous strand of microservices gives the total engineering team the ability and freedom to seek the most appropriate solution to a problem.
With multilingual microservices, it is important to understand that there is an increase of complexity with regards to deploy and stack maintenance. However, the compensation provided by the heterogeneous applications is very valuable.
Communication between teams, whether technical teams or business teams, is something that is, relatively complex. At other times, the technical communication between various teams such as frontend, backend, and mobile can be costly, and can delay some deliveries, commits, or the functionality of a feature. However, fluid communication with no noise is critical to the success of any project.
Writing good documentation, either in internal code or a simple document, is the best way to standardize knowledge among teams. The Swagger API is a good alternative solution for such problems:
With a simple configuration file, all of the APIs can be developed together with full integration between the team that develops requirements and the team that develops solutions.
As we have already seen with the published interfaces, it is now important to define the content of these endpoints and their size.
An important topic that is not always addressed is the componentization of the endpoints that are exposed. Think about a microservice responsible for user information. Some development teams decided to create a large endpoint that provides all possible information stored about a user. This type of endpoint, one
getUser type, may seem simple for development, but not for scalability.
A great deal of useless information for those who consume the API may be being passed, or is heavily specific information to transmit and expensive to be generated by the microservice. Thinking practically, the most sensible approach is to create an information API more fragmented and diverse, and if a
getUser is necessary, create an orchestrator of the smaller information and pass on a single endpoint. The following diagram is a good example of this:
This type of strategy is called endpoint builder, where the heavy point of information actually is compositions of other lighter data sources.
The componentization endpoints providing a lightweight and mature API for mobile applications is critical to business success. No one wants the battery completely consumed because an application has expensive endpoints.
With the mobile ecosystem, APIs with a
getUser, as mentioned in the preceding topic, are totally impractical. The definition of the limits of a microservice is not just what constitutes the microservice domain, but also exposing of the data of this domain.
When we speak of cache at the client level, it means that the request only passes to be processed on the backend, if really necessary. In other words, it tries to block direct access to the backend to requests that have already been implemented in the recent past.
A very useful tool for this strategy is the Varnish Cache, defined as: the Varnish Cache accelerator is a web application also known as reverse HTTP proxy caching. In the following diagram, we can see the operation of Varnish Cache:
The requests come from various types of web clients. Varnish Cache passes the request to the Server the first time only and stores the received data from the Server. If a second request for the same information already in the Varnish Cache is made, then Varnish Cache will answer the request, leaving the Server free of such access.
The Varnish Cache can store a number of different types of information in memory, but it is critical and targets the transmitted data. If the information is not componentized, Varnish always let the request go to the Server; you will have no way of knowing if the request is the same type.
Microservices that are well designed are highly scalable, but it does mean having infinite resources. With cloud-computer-limited resources, it is very relative, but the cost to provide a service can become so high that it prevents the same.
Thinking about it, some steps can be taken to reduce the cost of consumption. One, as mentioned earlier, is the implementation of an efficient cache. However, that's not all; at times, throttling is necessary to block the high consumption of resources.
It is not feasible that a client of a microservice as a web page runs a very high number of requests for the microservice, or that the same page is not mature enough to handle data already received.
For this, simple throttling that keeps the reference of who consumes the information and the data transferred to the client is very effective for reducing the consumption of microservices.
Some throttling policies can be applied:
- Number of requests per minute from the same client
- Number of requests per second from the same client
- Number of requests per minute from the same client for similar information
- Number of requests per second for the same client for the same information
With these, it is possible to limit such potential blunders as inadequate data manipulation, irresponsible Ajax requests, and less sophisticated attack attempts.
The identification of an anemic domain can be done by making a few simple observations:
- The microservice cannot perform the tasks itself with only the data received
- The microservice needs to fetch data in more than one endpoint to perform a task
- The microservice does not have a self-sufficient entity model
- The microservice waits for the completion of a task in another microservice to follow up what you need to do
- The microservice needs to share resources with other external microservices; these resources can be cached to the sample database
If the microservice being developed is one of those items, then it can be a weak area. If a microservice has two or more characteristics of those listed, then it is definitely an anemic domain.
Anemic domains are very harmful to the microservices ecosystem, because they have a tendency to be multiplied in order to correct the technical debt generated by the deficiency in the composition of their respective domains.
In many cases, the microservices perform more tasks than they should. Apparently all is well and deployment is simplified, but in fact, the domain is fat. Microservices do not have that name because they are a small application, but because they have a small and simple business domain. When a microservice has limitations in certain fields, it means that the application was initially constructed on a small monolith.
Thinking about our application, the news portal, a good candidate for a microservice is users. It makes perfect sense to build a microservice administering user data. However, usually, in a monolithic application, the layer on the users has a strong connection with AAA (Authentication, Authorization, and Accounting).
When it comes to microservice data, users, and AAA are an undesired coupling. This is mainly because the whole process of AAA is not restricted only to end users, but for clients such as mobile, frontend, and consumer APIs. In this case, the User microservice represents a fat domain.
The division of this fat domain can be held in two parts; the first part is
AAAService and the second is
UserService. Another approach is the AAA responsibility for a gateway API. The functional scalability and features of implementation with these separate domains is much more interesting for the growth of the product as a whole.
Understanding the size and limits of the domains is clearly critical to the growth and scalability of the final product.
This is the time to understand the business domain that will be developed in the book. The domains are contained in our monolithic application. Let's recap how it is composed. Our monolithic Django is organized into three Django apps that are as follows:
It is important to understand that in this context, because of how Django is designed,
Users and AAA are coupled, and we have seen that this is not good when it comes to microservices.
Another point is that news will not necessarily result in a single microservice; we can create microservices-varied news with the type of news. This would facilitate the targeting of APIs and scalability for each different type of news content. On our portal, we have sports, politics, and celebrity news. If a new theme is developed, a new
News microservice will be created for this theme. This approach enables something like z-axis scalability for that part of the application.
At first, our domains will be divided into the following categories:
Of course, new fields can be added and others can be removed; limiting the view of this microservice is our big target.
Given the domains that we have in our application, it is time to define the entities. When we speak of entities microservices it is important to note that any transactional need among microservices can mean a design error.
A process asynchronous message by the broker can be used to sanitize the database, but that does not mean that there is a transaction. Trying to establish a type of transaction between microservices that are completely separated may be a big mistake.
Our old application had the following entities:
- ID –
- Author –
- Labels – News subjects
- Type – New type (Sports, Famous, Politics)
- ID –
In addition to these entities, there is a range of tables that complement the user's information for the purpose of providing permissions and access permissions.
First, we know that all the news segments will not be unique. This implies the removal of the Type:
- News Service:
Another change is that users will no longer have the responsibility for authentication and authorization.
The goal of this chapter was to show you the basics of the development of a scalable microservice architecture, either from the point of view of the fields or from a strictly technical perspective.
We have addressed topics such as using Domain-Driven Design in microservices, The Scale Cube, the single-responsibility principle, and published interfaces in order to provide the minimum required theoretical knowledge, so you can apply it in a practical way in the upcoming chapters.
In the next chapter, we'll begin defining our stack using the concepts that we have already learned so far.