Django microservice architecture - evolution from monolithic to distributed system | Daoman PythonAI
#django microservice architecture - evolution from monolithic to distributed system
📂 Stage: Part 3 - Advanced Topics 🎯 Difficulty Level: Expert ⏰ Estimated study time: 8-10 hours 🎒 Prerequisite knowledge: 部署最佳实践, 性能优化
Table of contents
Microservice architecture overview
What is microservices
Microservices is an architectural style that splits a huge monolithic application into a set of small services that run independently. Each service is built around specific business capabilities and collaborates with each other through lightweight communication protocols (such as HTTP/REST or gRPC). Its core concept is "divide and conquer" to make complex systems manageable.
Different from the traditional layered architecture (where all logic is stuffed into a code warehouse), microservices emphasize:
- Organize teams by business: No longer divide teams according to technical lines such as front-end/back-end/database, but let one team be responsible for a complete business area (such as user team, order team) end-to-end, and can make independent decisions and quickly iterate.
- Independent deployment and expansion: A problem with one service will not bring down the entire system; whichever service is under great pressure, only add machines to that service, resulting in higher resource utilization.
- Freedom of Technology Heterogeneity: Service languages, frameworks, and databases can all be selected on demand and are no longer hijacked by a single technology stack. For example, the order service can use django + PostgreSQL, but the recommendation service uses Python + Redis.
When are microservices needed?
Not all projects are suitable for microservices. The value of microservices will only be reflected when your system has the following characteristics:
- Large and complex businesses: such as e-commerce platforms and SaaS products, with many functional modules and high coupling.
- Multi-team parallel development: Teams in different cities or even different time zones need to develop independently and do not want to wait for each other.
- Fast-changing business needs: A certain function needs to be frequently modified and launched without affecting the overall stability.
- Single application left over from history: The code has become so bloated that it is difficult to maintain and needs to be gradually refactored.
Realistic challenges that need to be faced
Microservices are not a silver bullet and introduce additional complexity:
- Difficulty in distributed debugging: A request may pass through five or six services, and locating the problem requires complete link tracking.
- Data consistency problem: It is difficult to rely on transactions in one database to ensure strong consistency when writing across services.
- Surge in operation and maintenance costs: Logging, monitoring, health checks, and configuration management all require specialized infrastructure, which places higher demands on the team.
These challenges require us to plan response strategies at the beginning of the design instead of patching them later.
Service split strategy
Splitting principle
How to dismantle it is a good question. It is recommended to refer to the bounded context idea in Domain Driven Design (DDD), combined with the following principles:
- Bounded Context: Define clear business boundaries for each service. The service only handles matters within its own domain and does not cross the boundaries.
- Independent data sovereignty: Each service has its own database and can only expose data to the outside world through API. Direct cross-database query in the code is never allowed.
- Single Responsibility: A service only solves one core business problem to avoid becoming a "mini monomer".
- High cohesion, low coupling: The internal logic of services is closely related, and the dependencies between services are as small as possible and one-way.
Example of e-commerce system splitting
Let’s use the most common e-commerce platform as an example of splitting. Several core services can be separated out:
- User Service: registration/login, rights management, personal information maintenance
- Commodity Services: Product information management, classification, inventory control
- Order Service: Order generation, status transfer
- Payment Service: third-party payment and refund processing
- Notification Service: Email, SMS, and site message sending
Each service is an independent django project with its own database and deployment unit.
In the order service model, we will not create a pointer to the user table or product table.ForeignKey, but only saves the ID association, because those tables are in other databases and cannot be connected at all:
This approach forces us to obtain user or product details through API calls. Although there is an extra step of remote calling, it results in complete decoupling of the service and data autonomy.
API gateway design
Why do I need a gateway?
After microservices are deployed, the number of services increases and addresses change frequently (especially when using containers). If the client directly interacts with each service, it will face a lot of trouble: it needs to remember a bunch of addresses, deal with differences in authentication methods, and write complex fault-tolerant logic. API Gateway is the unified entrance for each service. It is like a reverse proxy and is responsible for:
- Request Routing: Forward the request to the corresponding backend service based on the URL prefix, such as
/api/users/Go to User Services. - Unified Authentication: Complete JWT or OAuth2 verification at the gateway layer, and then put the parsed user information in the request header and transparently transmit it to the internal service, so that the backend does not need to repeat verification.
- Traffic Control: Current limiting, circuit breaker, and downgrade to protect the backend from being overwhelmed by sudden traffic.
- Protocol conversion: Provide RESTful interface externally, and call services internally through gRPC or other protocols.
Implement a simple gateway in django
We can use django REST Framework to quickly build a lightweight gateway. The core logic is to maintain a routing mapping table and forward requests.
The actual production environment will mostly use mature solutions (Nginx, Kong, Envoy, etc.), but building it yourself with django can give you maximum control over the gateway behavior, and it also facilitates quick verification of the architecture in the early stages.
Service communication mechanism
Microservices need to "talk" between each other, and there are two common modes: synchronous and asynchronous.
Synchronous communication: REST/gRPC
Suitable for scenarios where immediate results are required. For example, before a user places an order, the order service needs to query the user's balance or coupons in real time. At this time, it must wait synchronously.
Here is a simple client-side wrapper for synchronous calls:
Synchronous calling is intuitive and simple, but it creates strong dependencies on the caller and can easily cause cascading failures (service A times out, causing service B to also hang up). So be sure to set a timeout and use it with a fuse.
Asynchronous communication: message queue
Suitable for scenarios such as decoupling, improving throughput, and achieving eventual consistency. For example, after an order is generated, the user needs to be notified and inventory deducted. These operations do not have to block the order creation process and can be triggered asynchronously through events.
Based on Redis's publish/subscribe and list, a lightweight message queue can be built:
After the order service creates an order, it publishes anorder_createdEvents, notification services and inventory services can consume it asynchronously:
Distributed data management
Selection of consistency model
In a monolithic system, database transactions can easily ensure that data and business status are consistent. When it comes to distributed systems, according to the CAP theorem, we must make a choice between consistency (C), availability (A), and partition fault tolerance (P). Most Internet businesses will choose eventual consistency (BASE theory), which allows short-term inconsistencies, but all node data will reach consensus after a certain period of time.
Saga pattern: a powerful tool for achieving eventual consistency
Saga is the mainstream pattern for handling cross-service distributed transactions in microservices. It splits a large transaction into a series of local atomic transactions. After each transaction is completed, the next transaction is triggered through a message; if any step fails, a series of compensation operations are performed to roll back the previous operation.
Take the e-commerce order process as an example:
- Step 1: Withholding inventory of goods and services
- Step 2: User service deduction balance
- Step 3: The order service changes the order status to "Paid"
If the deduction of the balance fails, compensation is required: the withheld inventory is released and the order is closed.
Here is a simplified Saga implementation:
Note: The internal operations of each service here (such as deducting inventory and deducting balances) are still their own local database transactions. They are strung together through the coordinator (Saga), and global final consistency is guaranteed through compensation. This model must handle issues such as idempotence and null compensation. In practice, it is often paired with event sourcing or distributed transaction frameworks (such as Seata) to reduce complexity.
Migration strategy
To smoothly migrate a running single application to microservices, you cannot “replace it and start over”. Strangler Mode is recognized as the safest method in the industry.
The core idea of the Strangler mode
- New functions are directly uploaded to microservices: All new functions are implemented with independent microservices and mounted next to the existing system through the API gateway.
- Gradual replacement of old functions: Start with peripheral, low-risk functions (such as notifications, evaluation systems), and gradually advance to core modules (products, orders). Only move a small part at a time and verify that it is stable before continuing.
- Unified entrance traffic diversion: Through the gateway, traffic is dynamically switched to new microservices. Once a problem occurs, it can be quickly switched back to the single entity.
Phased implementation recommendations
- Check out the existing system: Figure out the boundaries, data dependencies and call links of each module in the monomer.
- Build infrastructure: First prepare the bases such as API gateway, service registration and discovery, centralized logging and monitoring.
- Split edge services: Choose a module that is least coupled with the core business to practice, such as notification services, and accumulate experience in microservice development and operation and maintenance.
- Gradually split the core: Core services such as products and orders will be migrated step by step, and data will be double-written or synchronized throughout the process to ensure that the data in the old and new systems are consistent.
- Monolith offline: When all traffic is cut to the microservices and runs stably for a period of time, the monolithic application is officially retired.
Summary of this chapter
In this chapter, we systematically sort out the key knowledge points of Django microservice architecture:
- Service Splitting: Based on the bounded context of domain-driven design, each service is only responsible for a piece of business and has independent data storage.
- API Gateway: Provides a unified entrance for all services, implements routing, authentication, current limiting and other functions, and isolates the client from internal details.
- Service communication: REST or gRPC is used for synchronous calls, and message queue is used for asynchronous calls, to cope with immediate needs and eventual consistency scenarios respectively.
- Data Consistency: Give up strong consistency, embrace eventual consistency, and achieve reliable distributed transaction compensation through Saga mode.
- Migration Strategy: Adopt the strangler model and gradually replace from edge to core to minimize risks.
💡 Core Point: Microservices are a means to solve complex system management problems, not a panacea. It is a reasonable choice only when the size of the team, business complexity, and operation and maintenance capabilities are sufficient to support its costs. Don't use microservices for the sake of microservices - starting from a singleton and evolving when you really need it is often the most pragmatic path.

