Case study
A billion-dollar monolith, taken apart
One PHP and Laravel monolith, behind a service earning a billion dollars a year. We did the whole move to microservices: the architecture, the event streaming, the data pipeline, the developer tooling, and the harder part, getting eight teams on board.
- $1B a year through the service
- 8 teams moved onto microservices
- 12 bounded contexts
Finding the seams
A monolith doesn't come apart along its folders. We drew the new boundaries around the business: twelve bounded contexts, each able to change without asking the other eleven for permission.
Placing those lines was the real work: cut in the wrong place and two services end up changing together, which is a monolith with extra network calls. Once the boundaries held, the contexts stopped reaching into each other's code and started exchanging events, so each one only has to say what happened, not who needs to hear about it.
Why Kafka
RabbitMQ SQS Chosen: Kafka
We didn't choose the broker out of habit. We put Kafka, RabbitMQ and SQS side by side, and two things settled it: a lot of data, and a lot of consumers. Kafka keeps events in a log instead of handing each one out once, so every consumer reads at its own pace, and a team that turns up next year can still start from the beginning.
| Requirement | Kafka | RabbitMQ | SQS |
|---|---|---|---|
| High data volume | Built for it: a partitioned log | Works, gets harder as throughput grows | Scales, but handled and priced per message |
| Many consumers | Consumer groups read independently | One queue per consumer | Needs SNS and one queue per consumer |
| Reading history again | Retained, replay from any offset | Gone once consumed, in classic queues | Gone once deleted |
| Ordering | Per partition | Per queue | FIFO queues only |
Why events
REST Chosen: Event streaming
With REST, the caller has to know everyone who cares, and waits for them to answer. With events, a service says what happened and moves on. A new team subscribes, and nobody else changes a line.
Why Avro
JSON Protobuf Chosen: Avro
JSON is friendly until a field changes and a consumer three teams away finds out in production. Protobuf was the closest call. Avro with Confluent Schema Registry gave us compact messages and a contract that gets checked on every schema change.
Into Snowflake
The data analysts already did their work in Snowflake, so nobody needed a new place to run their analysis. And the events now travelling through Kafka were ideal raw material for it: the activity of the business as it happened, structured and backed by a schema.
All that was missing was the link between the two. There was no off-the-shelf shortcut here, so we wrote a custom driver that carries the data from Kafka into Snowflake, where the analysts can take it further with the tools they know.
Production on a laptop
Twelve contexts are no use if nobody can run them. We built a complete local environment for developers, with production services mocked, on Docker, Node.js and Google zx, and with Confluent Schema Registry and Avro in the loop just as they are in production.
Eight teams had to believe it
The hardest migration wasn't in the code.
Eight teams don't change how they work because a diagram says so. We onboarded them, ran the training and kept explaining the why: what microservices are good at, where they hurt, and when they're the wrong answer. Nobody was sold a silver bullet, which is why it stuck.
Where they're strong
- Teams ship without waiting for each other
- A failure can stay inside one service
- Each part scales on its own
- New consumers don't touch the producer
Where they hurt
- A function call becomes a network call that can fail
- Data is eventually consistent, not instantly
- Debugging crosses service boundaries
- There's a lot more to deploy, watch and pay for









