Service Splitting
Li Wei
Service Splitting
Overview
Understanding the Concepts
Monolithic vs. Distributed Architecture
Monolithic structure: As the name suggests, all functional modules of a project are developed within a single codebase. When deploying, all modules must be compiled and packaged together. The architectural design and development model are very simple.
When the project is small, this approach is quick to start with, and deployment and operations are straightforward, so many early‑stage small projects adopt it.
However, as the business scale grows and the development team expands, monolithic architecture begins to show many problems:
- High team collaboration cost: Imagine dozens of developers working on the same project. Because all modules reside in one codebase, the physical boundaries between them become blurry. Merging features into a single branch inevitably leads to a swamp of merge conflicts.
- Low release efficiency: Any change in a module requires releasing the entire system. The release process involves many inter‑module constraints, file comparisons, and any single issue can cause the whole deployment to fail, often taking tens of minutes or even hours.
- Poor system availability: In a monolith, all functional modules are deployed as a single service, so they affect each other. Hotspot features can exhaust system resources, causing other services to become unreliable.
Microservice architecture first means “service‑ification”: extracting functional modules from the monolithic application and deploying them as independent services.
It should also satisfy the following characteristics:
- Single responsibility: A microservice handles a specific business function, and its core data does not depend on other modules.
- Team autonomy: Each microservice has its own development, testing, release, and operations staff, with team size not exceeding about 10 people (two pizzas can feed a team).
- Service autonomy: Each microservice is packaged and deployed independently, accesses its own database, and is isolated to avoid impacting other services.
For example, in the Black Horse Mall project we can split modules such as product, user, cart, and transaction, assign them to different teams, and deploy them independently.
Solving Monolithic Problems
- Because of service splitting, each service’s codebase is much smaller; typically 1–3 backend developers work on it, dramatically reducing collaboration cost.
- Each service is deployed independently; when a service changes, only that service needs to be packaged and deployed.
- Independent deployment with proper isolation means each service uses its own server resources and does not affect others.
Distributed vs. Microservices
Microservices and distributed systems are common concepts in software architecture; they share similarities but also differ.
Microservices
- Definition: A software architectural style where the system is built as a set of small, independent services. Each service runs in its own process and communicates via lightweight mechanisms, usually HTTP RESTful APIs. Each microservice focuses on a narrow task. Distributed architecture is essentially the process of splitting services; microservices are a best‑practice implementation of a distributed system.
- Characteristics:
- Small‑scale: Each service is relatively small and concentrates on a specific business function.
- Independence: Services can be deployed, scaled, and updated independently without affecting the rest of the system.
- Technology diversity: Different services may use different programming languages, frameworks, and tech stacks.
- Loose coupling: Services interact through APIs, keeping coupling low.
- Benefits:
- Faster release cycles: Independent releases enable quicker feature delivery and bug fixes.
- Better scalability: Each service can be horizontally scaled on its own demand, without scaling the whole system.
- Greater flexibility: Teams can choose the most suitable technology stack for each service.
- Use cases: Complex, large‑scale applications that require rapid iteration and continuous delivery.
Distributed Systems
- Definition: A system composed of multiple computers that communicate over a network to accomplish a common task or provide a service.
- Characteristics:
- Multiple nodes: Consist of several physical machines or virtual machines.
- Heterogeneity: Nodes may run different hardware, operating systems, or software.
- Communication: Nodes interact via network protocols (LAN, WAN, Internet).
- Benefits:
- High availability: Failure of some nodes does not bring down the whole system.
- Performance scaling: Adding nodes increases capacity and throughput.
- Load balancing: Work can be distributed across nodes to avoid bottlenecks.
- Use cases: Scenarios demanding high availability, high performance, and load balancing, such as large websites and cloud platforms.
Differences
Although microservice architecture usually runs on a distributed system, there are distinctions:
- Granularity: Microservices focus on small, business‑level services; distributed systems can contain components of any size.
- Communication style: Microservices typically use HTTP RESTful APIs, while distributed systems may employ a variety of protocols and technologies.
- Design goal: Microservices aim to accelerate software development and delivery; distributed systems aim to improve system availability, performance, and scalability.
Spring Cloud
Spring Cloud is an all‑in‑one solution for distributed microservices, bundling many microservice‑enabling technologies—often called the “microservice family bucket.”
Built on Spring Boot’s auto‑configuration capabilities, it greatly reduces the cost of setting up projects and using components.
Official site: https://spring.io/projects/spring-cloud#overview
Spring Cloud Alibaba is Alibaba’s microservice solution, providing key capabilities such as service discovery and configuration management that are not covered by the open‑source Spring Cloud components. It includes five major modules.
Microservice Technology Comparison
(Insert comparison table or diagram here)
Service Splitting Principles
When splitting services, consider these questions:
- When to split?
- How to split?
When to Split
For a brand‑new project, the first priority is to validate feasibility. This stage emphasizes agile development and rapid delivery of a market‑ready product for validation. Consequently, the architecture is usually kept simple—often a monolith—because it lowers development cost and speeds up results. If the market rejects the product, the loss is minimal.
If a complex microservice architecture is adopted at this early stage, a lot of time and manpower are spent on architectural design, only to discover later that the product doesn’t fit the market—essentially wasted effort.
Therefore, most small projects start with a monolith and later transition to a microservice architecture as user numbers and business complexity grow. This keeps early costs low and allows quick experimentation, but it can make later splitting harder due to tight code coupling (easy first, hard later).
Conversely, for large‑scale projects whose long‑term goals are clear from the outset, teams may choose microservices from the beginning. Although the upfront investment is higher, later service‑splitting headaches are avoided (hard first, easy later).
How to Split
We previously said that microservice granularity should be small—that is the splitting goal. It can be analyzed from two angles:
- High cohesion: Each microservice should have a single responsibility, with business logic that is highly related and complete internally. The aim is that when a business rule changes, only the relevant microservice needs modification, reducing change cost.
- Low coupling: Each microservice’s functionality should be relatively independent, minimizing dependencies on other services, or at least depending on stable interfaces.
High cohesion starts with a single responsibility, but it does not mean “one interface per service.” The internal business must be complete. Once a service achieves high cohesion, the coupling between services naturally drops.
Some business interactions are unavoidable—for example, an order service needs product data. The order service should not query the product database directly, which would create data coupling. Instead, the product service should expose an API, and the order service should rely on that stable interface. Even if the product service changes internally, the order service remains unaffected, reducing coupling.
Having clarified the splitting goals, the actual splitting methods fall into two categories:
- Vertical splitting
- Horizontal splitting
Vertical splitting divides the system by functional modules. In the Black Horse Mall example, modules such as user management, order management, cart, product management, and payment can each become a separate service. This approach maximizes service cohesion.
Horizontal splitting looks for common business functions shared across modules and extracts them as shared services. For instance, both user login and order placement need to send notifications and record risk‑control data. Messaging and risk‑control can therefore be extracted into common services: a Message Center service and a Risk Management service. This improves reuse and avoids duplicate development. Common services usually have stable interfaces, preventing excessive coupling.
Service Invocation
After splitting services, cross‑service business logic inevitably arises, requiring remote calls between microservices. Remote calls are known as RPC (Remote Procedure Call). RPC can be implemented in many ways, such as:
- HTTP‑based
- Dubbo protocol
Spring provides a RestTemplate API that makes sending HTTP requests easy.
org.springframework.web.client.RestTemplateextendsInterceptingHttpAccessorimplementsRestOperations— a synchronous client for executing HTTP requests. It offers a simple template‑method API over underlying HTTP client libraries (e.g., JDKHttpURLConnection, Apache HttpComponents).RestTemplateprovides convenient methods for common scenarios and generic exchange/execution methods for less common cases. It is typically used as a shared component, but its configuration is not thread‑safe for concurrent modification, so it is usually prepared at startup. Multiple differently configuredRestTemplateinstances can be created at startup; if they need to share HTTP client resources, they can use the same underlyingClientHttpRequestFactory. Note: Since version 5.0 this class is in maintenance mode; only critical bug fixes are accepted. Consider usingorg.springframework.web.reactive.client.WebClient, which offers a more modern API supporting synchronous, asynchronous, and streaming scenarios. — Since 3.0. See also:HttpMessageConverter,RequestCallback,ResponseExtractor,ResponseErrorHandler.
RestTemplate offers many methods for sending HTTP requests, for example:
You can see that common GET, POST, PUT, DELETE requests are supported. For more complex request parameters, the exchange method can be used to construct the request.
- First, register
RestTemplateas a Bean: - Then invoke remote calls
Common RestTemplate API methods include:
getForObject: Send a GET request and return an object of the specified typePostForObject: Send a POST request and return an object of the specified typeput: Send a PUT requestdelete: Send a DELETE requestexchange: Send a request of any type and return aResponseEntity
Originally written by Li Wei (李唯_) and published in Chinese on 后端技术栈全书 (Full-Stack Backend Engineering). Translated and adapted for DriftSeas with permission.