From Monolith to Microservices: A Practical Journey of an Online Supermarket
This article walks through the evolution of a simple online supermarket from a monolithic architecture to a fully fledged microservice system, highlighting the challenges, component choices, database splitting, monitoring, tracing, logging, gateway, service discovery, resilience patterns, testing strategies, and the trade‑offs of using a custom framework versus a service mesh.
As a website grows, a monolithic application often cannot meet the requirements, prompting a shift toward distributed microservice architecture.
Initial Requirements
A few years ago, Xiao Ming and Xiao Pi built an online supermarket with a simple website for browsing and purchasing, plus an admin backend for managing users, products, and orders.
Website: user registration/login, product display, ordering
Admin backend: user management, product management, order management
The initial implementation was straightforward, with the website and admin backend deployed separately on a cloud server.
Business Growth
Rapid competition forced the team to add promotions, mobile channels, and data‑driven personalization, leading to a rushed expansion where promotional and analytics features were crammed into the admin backend and a new mobile app was built.
Promotions (e.g., holiday discounts)
New mobile app and WeChat mini‑program
Data analysis for personalized services
This ad‑hoc expansion introduced many problems: duplicated code, tangled API calls, oversized services, performance bottlenecks, shared database contention, and difficult deployments.
Time for Change
Realizing the architectural debt, the team abstracted common business capabilities into dedicated services: User Service, Product Service, Promotion Service, Order Service, and Data‑Analysis Service.
Each application now consumes these services, reducing redundant code and leaving only thin controllers and front‑ends.
Database Split
Even after separating services, a shared database remained, causing performance bottlenecks, schema coupling, and single‑point failures. The team therefore isolated persistence per service and introduced a message queue for real‑time communication.
Monitoring – Detecting Faults
To catch issues early, each component exposes a uniform metrics endpoint. Prometheus scrapes these metrics, and Grafana visualizes them with alerts. Open‑source exporters (e.g., RedisExporter, MySQLExporter) provide ready‑made metrics for common services.
Tracing – Link Tracing
Requests traverse multiple services, so the team added a tracing interceptor that injects traceId, spanId, parentId, and timestamps into HTTP headers. Collected spans are sent to Zipkin for visualization, enabling identification of slow or failing services.
Log Analysis
Large, distributed logs require a searchable store. The team adopted the ELK stack (Elasticsearch, Logstash, Kibana): Logstash gathers logs from each service, Elasticsearch indexes them, and Kibana provides query and dashboard capabilities.
Gateway – Access Control & Service Governance
A gateway sits between callers and services to enforce authentication, authorization, and routing. The team chose a coarse‑grained approach: one gateway for external traffic, while internal calls remain direct.
Service Registration & Discovery – Dynamic Scaling
To automate instance management, services register themselves with a discovery system (e.g., Zookeeper, Eureka, Consul, Etcd). Clients retrieve service addresses from the registry, enabling load‑balanced calls and seamless scaling.
Circuit Breaker, Service Degradation, Rate Limiting
If a downstream service becomes unresponsive, a circuit breaker trips to prevent cascading delays. Non‑critical services can be degraded to preserve core functionality, and rate limiting protects services from overload during spikes.
Testing
Testing is organized into three layers: end‑to‑end tests for critical user flows, service‑level tests for API contracts, and unit tests for individual code units. Mock servers help isolate service dependencies during testing.
Microservice Framework
The team built a shared framework that encapsulates integration code for metrics, tracing, logging, registration, routing, circuit breaking, and rate limiting, applying the DRY principle across services.
However, framework upgrades require coordinated releases across all services, so version management and compatibility strategies are essential.
Another Path – Service Mesh
Instead of embedding logic in each service, a sidecar proxy can handle cross‑cutting concerns. The data plane (sidecars) processes traffic, while a control plane manages configuration. Service mesh offers non‑intrusive upgrades but may add latency.
Conclusion – Not the End
Microservices are a significant step, not a final destination; future directions include serverless, FaaS, and even revisiting monoliths. The journey illustrates both the benefits and the new complexities introduced by microservice architectures.
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.
Liangxu Linux
Liangxu, a self‑taught IT professional now working as a Linux development engineer at a Fortune 500 multinational, shares extensive Linux knowledge—fundamentals, applications, tools, plus Git, databases, Raspberry Pi, etc. (Reply “Linux” to receive essential resources.)
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.
