0% completed
Strangler Pattern: A Detailed Example
On This Page
The Four Operations
The Three Parts
The Routing Table
Choosing Which Customers Move
Reading the Trace
One Store, Two Systems
A Note on the Hash
Your Turn
What to Take Away
An online store runs on one large application. It shows the catalog, keeps the cart, takes orders and prints invoices. The team wants smaller services instead. The store has to keep selling while they build them.
This lesson builds that migration as code you can run. Change the routing table, press Run, and watch the traffic move.
The Four Operations
The store answers four kinds of request.
getProductreads one product from the catalog.searchProductsfinds products by name.getOrderHistorylists what a customer has ordered.placeOrdercreates a new order.
The Three Parts
Each part of the example maps to a part of the pattern.
LegacyOrderSystemis the monolith. One class answers all four operations.NewOrderPlatformis the replacement. It holds two focused services,CatalogServiceandOrderService.StranglerFacadesits in front of both. Every request enters here.
The facade is the only component that knows a migration is running. Callers see one entry point. They cannot tell which system answered.
The Routing Table
The facade holds a routing table. Each row names an operation and a percentage. The percentage is the share of that operation's traffic the new services answer.
| Table row | Meaning |
|---|---|
| No row at all | Never migrated. The monolith keeps it. |
getProduct = 100 | Every request goes to the new service. |
getOrderHistory = 50 | Half the customers go to the new service. |
placeOrder = 0 | Rebuilt, then withdrawn. Nobody goes across. |
Moving a piece of work means editing this table. It does not mean editing the callers, and it does not mean a release.
A missing row and a row set to 0 both send traffic to the monolith. They still mean different things. A missing row has never been migrated. A row at 0 was migrated and then withdrawn.
Choosing Which Customers Move
A percentage needs a rule for which customers it covers. Random choice would be wrong here. A customer sent to the new service on one request and the old one on the next would see two systems in one session.
So the facade hashes the operation and the customer id together. That gives a bucket from 0 to 99. The same customer and operation always produce the same bucket. That customer stays on one side until the percentage changes.
Hashing the operation as well as the customer keeps the rollouts independent. One customer can sit inside the half for order history and outside the quarter for placing orders.
Reading the Trace
Each phase prints the routing table, then one line per request, then a tally.
Phase 1 has an empty table, so the monolith answers everything. Phase 2 moves the catalog read across. Phase 3 puts half the customers on the new order history service. The output shows henry moving while alice stays.
Phase 4 starts placeOrder at 25 percent. Phase 5 is the one to study. The rollout looked wrong, so placeOrder went back to 0. Traffic on the new services falls from 66 percent to 50 percent. No code was deployed. No data was moved. One number in the table changed.
Phase 6 finishes the work. The monolith answers nothing and can be switched off.
One Store, Two Systems
Both systems read and write the same OrderStore. That detail carries more weight than it first appears to.
In phase 4 the new service takes order 1007 for henry. In phase 5 henry reads his history, and the count includes orders the monolith took earlier. Neither side keeps a private copy.
Most real migrations do not stay this simple. The new services usually want their own schema. Until they have one, both systems share a database, and the pattern holds only while both sides agree on what the data means. Splitting that store is separate work, and it is often the hardest part of the migration.
A Note on the Hash
The bucket function here is deliberately small, so that all six languages agree on every answer. It is not a strong hash. Two customer ids that differ by one character can produce buckets that sit next to each other. Production rollouts use a stronger hash such as MurmurHash for that reason.
Your Turn
The facade does four things. It reads the table, falls back to the monolith, hashes the customer, and compares the bucket against the percentage. The exercise below asks you to write that decision.
routeRequests takes the routing table as two matching arrays, plus a list of requests. Each request is a pair: the operation and the customer id. Return one decision per request, in the same order, either new or legacy.
The bucketFor helper is written for you. The rules are:
- An operation with no entry in the table goes to
legacy. - Otherwise compute the bucket for
operation + ":" + user. - Answer
newwhen that bucket is below the percentage, andlegacywhen it is not.
What to Take Away
- The facade is one component, and it is the only place that decides.
- The decision reads configuration, not code. A migration step is an edit to a table.
- An operation with no rule stays on the monolith. That is the safe default.
- A percentage plus a stable hash gives a gradual move and a steady experience.
- Rollback costs one number. That is what makes each step safe to attempt.
- Both systems share the data until you split it, and that split is separate work.
The order of the phases is a rule of the pattern, not an accident. Read-only operations moved first, because a wrong answer there is easy to spot. A bad read also cannot damage stored data. Writes moved last, one at a time, at a small percentage.
The example keeps the facade in memory so it runs in one file. A real facade reads its table from a configuration service instead. The table then changes without a deployment, and that is the property the pattern needs. If every step costs a release, the team stops taking steps.
Reading Progress
0%
On This Page
The Four Operations
The Three Parts
The Routing Table
Choosing Which Customers Move
Reading the Trace
One Store, Two Systems
A Note on the Hash
Your Turn
What to Take Away