From MVC to Microservices: Lessons from Evolving a Python SaaS Backend
The author recounts a four‑stage evolution of a Python‑based SaaS backend—from a simple Django MVC prototype through service splitting, microservice adoption with Kong and custom RPC, to an exploratory domain‑driven design—highlighting practical problems, optimizations, and architectural trade‑offs.
1. MVC
In the early stage the team (max 5 developers) built a prototype quickly with Django, focusing on code conventions and view abstraction while leaving room for product changes. The architecture was simple: Nginx for load balancing, multiple Django instances, Celery for async tasks, Redis for caching, and Nginx Push Module for real‑time notifications.
Problems and optimizations:
Django’s concurrency limits were addressed by deploying uWSGI Master+Worker together with gevent for coroutine‑based high concurrency.
Excessive Redis connections were mitigated by using redis‑py’s built‑in connection pool for reuse.
MySQL connection overload was solved with the djorm‑ext‑pool connection‑pooling extension.
Celery was configured to use gevent, enabling concurrent task execution.
2. Service Splitting
As the backend team grew, responsibilities were divided into finer‑grained services. The existing Django project already organized code into high‑cohesion, low‑coupling apps, which eased the split. Initial separation involved extracting shared code into a common Python library while keeping the same database and Redis instances; later, multiple database instances were added to handle load.
Inter‑service communication was kept to a minimum; when needed, services called each other via HTTP, using internal host entries for address resolution.
Problems and optimizations:
The long‑unmaintained Nginx Push Module could not handle enough persistent connections, so a Tornado + ZeroMQ based service (tormq) was built to provide reliable notifications.
Additional pain points included manual host‑file management for service endpoints, lack of retry/error handling/traffic‑shaping in the call chain, and cumbersome Nginx configuration changes that often broke calls. Authentication relied on a central user‑center database, requiring coordinated redeployments when changed.
3. Microservice Architecture
The ingress layer adopted an OpenResty‑based Kong API Gateway, with custom plugins for authentication and rate limiting. Deployment scripts invoke Kong’s admin API to register new services and attach the required plugins.
To replace direct service‑to‑service calls, a gevent + msgpack RPC framework named doge was built, using etcd for service discovery and providing client‑side rate limiting, high availability, and load balancing.
Choosing an API gateway involved evaluating Golang and OpenResty implementations; Kong was selected for its Lua‑based plugin model, ease of customization, and out‑of‑the‑box features. Two Kong instances were run behind a cloud load balancer, forming a synchronized cluster.
Although the team considered a pure Python Thrift implementation (thriftpy) and the Motan framework from Weibo, limited manpower and varying skill levels led to the decision to develop the lightweight doge RPC solution instead.
4. Domain‑Driven Design
The next architectural step aimed to extract a dedicated data‑service layer. Each data service would host one or more bounded contexts, exposing a single aggregate root via RPC. Application services would depend on these data services but not vice‑versa, reducing coupling and allowing higher‑level services to remain independent of lower‑level implementations.
At the time of the author’s departure, DDD was still in the learning phase and had not been fully implemented, but the intention was to continue moving the backend toward this design.
Conclusion
Technology choices should serve product and customer needs rather than follow trends blindly; team composition and skill gaps inevitably force compromises, and the real challenge is finding optimal solutions within those constraints.
Emerging patterns such as Service Mesh are becoming mainstream, and the author plans to keep exploring them.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
ITPUB
Official ITPUB account sharing technical insights, community news, and exciting events.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
