Lesson 24 / 25

Case Study: An E-Commerce Platform Over Five Years

Follow an architecture evolving from monolith to selective services.

Evolving with the business

Year 1: three engineers build a shop as a single Django or Spring Boot application with PostgreSQL, deployed on two instances behind a load balancer. Speed of change matters most. Year 2: the team grows to 12; they reorganise the code into modules (catalogue, checkout, fulfilment, accounts) with enforced boundaries and schemas, keeping one deployment. Year 3: search traffic grows tenfold and needs Elasticsearch-style indexing and its own scaling; the team extracts search as the first service using the strangler pattern, fed by product events from the monolith via an outbox. Year 4: with 40 engineers in five teams, payments must meet stricter compliance and release independently; payments and notifications become services, and a platform team provides pipelines, Kubernetes and OpenTelemetry. Year 5: the core order flow remains in a well-structured monolith because it changes as a unit; a few chatty services that were split too early are merged back. The result is a pragmatic hybrid, shaped by real pressures.

The architecture in year five

A modular core with a handful of services where they earned their place.

clients -> CDN -> API gateway
                    |-> web BFF ----.
                    |-> mobile BFF -+-> core monolith (accounts, catalogue, checkout, fulfilment)
                    |                     |  modules with own schemas; outbox -> events
                    |-> search service  <-+  (product events) own index, scales alone
                    |-> payments service     PCI scope isolated, own team and DB
                    '-> notifications        consumes order events, email/SMS/push
platform: Kubernetes, CI/CD templates, OpenTelemetry, service catalogue, on-call per team

Each extraction had a named reason

Search: independent scaling and technology. Payments: compliance and release independence. Notifications: asynchronous, non-critical side effects. If you cannot name a reason like these, the module can stay in the monolith.

Quick check: In the case study, why did the core order flow stay in the monolith?

  • Because it changes as a unit and splitting it would add coordination and consistency costs without a clear benefit
  • Because monoliths cannot be split
  • Because Kubernetes was unavailable
  • Because services cannot handle orders
Answer

Because it changes as a unit and splitting it would add coordination and consistency costs without a clear benefit — Keeping tightly coupled logic together avoids distributed transactions and lockstep releases.