preface

A lot of people say that it’s not difficult to start micro-services, the difficulty is how to classify them. This statement, though a bit absolute, reflects how important service boundaries are. A well-designed monolithic application is far better than a chaotic microservice. The purpose of adopting microservice architecture is to make the system more scalable and usable. But before a single application can be turned into a solid microservice architecture, the modules of a single system should be reasonably and clearly divided. You can think of microservices as simply deploying individual modules of a single application separately.

This paper will discuss the division method of microservices from multiple perspectives.

The idea of defining boundaries

In object-oriented programming, there is a principle we all know: high cohesion, low coupling. In general, the higher the cohesion of each module in the program structure, the lower the coupling degree between modules. This basic principle guides both the modularization of individual applications and the partitioning of microservices.

Under the guidance of this abstract principle, combined with the practice of software design, we can find different methods of service partition.

Database driven design

The central premise of data-driven programming is that humans are better at processing data than program logic. Data is much easier to manipulate than program logic, so we should try to shift the complexity of our design from program code to data.

In the words of Rob Pike, a former Bell LABS fellow and member of the Unix team, data trumps everything. If you choose the right data structure and keep everything organized, the right algorithm speaks for itself. The core of programming is data structures, not algorithms.

Most of the projects I work on are database-centric. Data is stored centrally in different tables, and in order to satisfy relational model theory, the design of these tables needs to conform to various paradigms and standards. The business logic of the software system is realized by adding, deleting, changing and checking these tables.

Domain-driven design

Domain-driven design (DDD) is based on the idea of object oriented and reflects the abstraction of system through domain model, so as to get reasonable service division.

By analyzing the system from the perspective of business through DDD and abstracting the system, a set of business models with higher cohesion can be obtained. In DDD, a set of business models with similar concepts, high cohesion and clear boundaries can be found are called Bounded Context.

A bounded context can be considered a logical microservice or a module within a monolithic application. In the field of e-commerce, orders, goods and payment are the most common concepts in the field of e-commerce. In the social space it’s users, groups, messages, etc.

Data – based partitioning methods

Data-driven is a bottom-up approach to architecture design. Data-driven emphasizes data structure, that is, the overall data structure is determined by analyzing requirements and services are divided according to the relationship between tables.

The usual steps to divide services based on data drive are as follows:

  1. Requirements analysis. Identify goals with domain experts (or product managers) and then summarize the User Story to identify core business processes; Using tools to present a rough interface for internal discussion; Iterate until you are satisfied.

  2. Abstract data structures. Summarize Use cases according to requirements, assist in analyzing requirements, and abstract data structures from them.

  3. Partitioning services. Analyze data structures and identify services — services should meet characteristics such as high cohesion, low coupling, single responsibility SRP, etc.

  4. Determine the service invocation relationship. First analyze the main process, according to the request needs to call the service to determine the service invocation relationship. If there is a problem, you need to go back to (1) and start again.

  5. Business process validation. Go back to the User Story and realize the sequence diagram in terms of service granularity. Note that the focus of this stage is to verify whether the service partition is appropriate. Pay attention to the following issues.

    • What are the consistency requirements for an update operation that spans more services?
    • Whether to perform associated query across services and whether the problem can be resolved within one service.
    • Check whether the performance meets requirements.
    • Whether the cost meets the requirements.
  6. Continuous optimization.

Domain-based partitioning methods

Domain-driven architecture is a top-down approach to architectural design, through the establishment of a unified language with domain experts, continuous communication, identification of key business scenarios, and step-by-step identification of bounded contexts. The domain-driven emphasis on business implementation suggests that bottom-up design can lead to technical people not understanding the business direction better and thus deviating from business goals.

The usual steps for classifying services based on domain drivers are as follows:

  1. Establish a common language through models and domain experts. The common language was established to gain a deeper understanding of requirements. The general language should be mainly business language rather than technical language. Common language, like code, requires constant refactoring.

  2. Business analysis. Identify the core business processes and then expand to all. It is best to use tools to render a rough interface for internal discussion.

  3. Look for aggregations. Explicitly define the boundaries of the domain model. One of the hottest recent event storms is a domain-driven approach to analyzing business and partitioning services. An event storm is a meeting where all the key players get together in a large room and use post-it notes to describe what’s going on in the system, as shown below.

    • Use orange post-it notes to represent domain events, with a one-sentence description of what happened.
    • Use blue post-it notes to represent commands. The initiator of a command may be a human, an external event injected into the system, or a timer.
    • Use yellow post-it notes to represent aggregation. Aggregation is a set of related domain objects. High cohesion and low coupling are its basic requirements. Data consistency must be guaranteed in aggregation.

  1. Determine the service invocation relationship. First, analyze the main process and determine the service invocation relationship according to the services that need to be invoked in a request. If there is a horizontal division, the relationship needs to be determined according to the principle of service dependency. If there is a problem, you need to go back to (1) and start again.

  2. Business process validation. Realize the sequence diagram with service as granularity. Note that the focus of this stage is to verify whether the service division is appropriate. The following issues are mainly concerned.

    • What are the consistency requirements for an update operation that spans more services?
    • Whether to perform associated query across services and whether the problem can be resolved within one service.
    • Check whether the performance meets requirements.
    • Whether the cost meets the requirements.
  3. Continuous optimization.

Other auxiliary methods

Maintenance costs are lower after the split than before

Maintenance costs include manpower, material resources, and time.

The cost here is an important link that must be considered for most small and medium-sized teams. If the input is not proportional to the income, or exceeds the budget of the leader or the market window, then the advanced technology is a stumbling block. Do not be obsessed with technology, and the so-called engineer thinking must not be.

The split is not only an architectural adjustment, but also an adaptive optimization of the organizational structure

Ensure that the split services are maintained by a separate team.

How to understand this sentence? The traditional team division is horizontally divided according to the product department, front end and back end. After microservitization, the team may be the number of people who eat a slice of pizza. The product, front end and back end are classified into the service, and the number of people is allocated based on the service.

By team size

Make sure you don’t have too big or too small a development team for each microservice.

  • The “Three Musketeers” principle

    In principle, 3 people are responsible for one micro service. The principle of “three Musketeers” is mainly applied in the design and development stage of micro-services. If the micro-services have been stable after a period of development and are in the maintenance stage without too much development, it is ok for one person to maintain one or even several micro-services on average. Of course, considering the problem of personnel backup, it is best to arrange two people to maintain each micro-service, and each person can maintain multiple micro-services.

  • The two Pizzas rule

    The two-pizza principle was first proposed by Amazon CEO Jeff Bezos, who believed that if two pizzas didn’t feed a team, it might be too big. Because overpopulated team meetings are not conducive to decision-making, while small teams working together on projects are more likely to reach consensus and facilitate innovation within the company.

Antipatterns for service partitioning

By user role

Divide microservices by user roles using the system. Taking the e-commerce system as an example, it is a very undesirable method to divide it according to the roles of buyers, sellers, after-sale attendants and couriers. Experienced developers generally don’t fall into the obvious trap of different roles using the same business functions.

Other companies have divided their teams by channel, even dividing them into teams To C (for users) and To B (for internal enterprise), and finally designing “C-side article services” and “B-side article services” in a limited context. It is also a surprising architecture.

The smaller the granularity, the better

The size of the service is not particularly important and can be considered in terms of team size, code size, business complexity, technical domain, importance, cost, and so on. As for the size of service granularity, there is no unified standard in the industry, and it is difficult to measure. The closest measure is the size of the R&D team. Smaller granularity means higher maintenance costs. Back-end management and secondary systems are usually more granular.

Lump-sum service

When moving from a monolithic application to a microservice architecture, split the business code and database simultaneously. According to the services, the database is completely split into multiple independent databases. Each service has its own database service, and the services can only be called through interfaces. This looks great, but it requires a lot of data migration. When the business is in the early stage, the requirements are not very certain, and the developers do not have a thorough understanding of the business, they may find that the split is not reasonable, and they have to merge again, and then carry out data migration. Relatively speaking, business code splitting costs are lower, while data migration costs are higher, and frequent, massive data migration is not desirable.

Once a service partition is complete, it cannot be changed

It is difficult to make a perfect solution all at once due to the constant changes in the business and the developers’ understanding of domain knowledge and other influencing factors. Often, after a partition, a problem is found to be intolerable, such as reduced response time, increased costs, and the possibility of re-merging services; Due to business changes, the original dependencies have changed, and the services may need to be reclassified.

conclusion

In my opinion, domain-model-based microservice partitioning is a more desirable approach, but it is also a higher threshold in practice. It requires team members to have a deep understanding of DDD concepts and strong domain modeling skills, and as far as I know an experienced modeler is not available. In contrast, data-based partitioning, while at risk of deviating from the nature of the business, makes practical sense in situations where team members have limited capabilities.

reference

  • Learning architecture from 0 by Li Yunhua
  • Continuous Evolution of Cloud Native: Microservices Best Practices in Cloud Native Architecture, Qijun Wang