# How do I peel the payment module off a PHP monolith with Strangler Fig?

> Shift payments at the gateway by 5/20/100 and validate in shadow first; the real work is splitting the tables, behind an anti-corruption layer and outbox.

- Asked: 2026-05-09
- Answered: 2026-05-12
- Asked by: Onur
- Tags: mimari, mikroservis, go
- Source: https://www.muhammetsafak.com.tr/en/just-ask/peeling-the-payment-module-off-a-php-monolith-with-strangler-fig/
- Language: en-US
- Author: Muhammet Şafak

---
**Question:** I have a large PHP monolith. I want to peel off the "Payment" module and turn it into an independent Go microservice. I want to route traffic gradually (5%, 20%, 100%) to the new service without users or the existing code noticing.

How do I set this up at the API Gateway (Envoy/Nginx) level? How do I keep consistency while splitting the databases?


Short answer: put a facade/gateway (Nginx/Envoy) in front, shift traffic by weight (5/20/100), keep the monolith as fallback. But the hard part isn't routing — it's the data.

The logic of Strangler Fig is this: you don't kill the old module at once, you wrap it and slowly shift traffic to the new service. The order is:

1. **Set up weighted routing at the gateway.** Keep Nginx/Envoy in front, send payment traffic to the new Go service by weight `5% → 20% → 100%`, the rest to the monolith. If something blows up, the monolith fallback is ready; the rollback is one line of config.
2. **Run it in SHADOW first.** Before it handles real money, run the new service in shadow mode: mirror traffic, compare outputs against the monolith's, **take no real action**. If the numbers match, take it live. With money on the line there's no "hope it's right".
3. **NEVER share tables.** This is where the difficulty really is. Give the payment service its own store; don't co-write into the monolith's tables. Put an **anti-corruption layer** in between so the monolith's model doesn't leak into the new service.
4. **Keep consistency with outbox + events.** Don't reach for distributed transactions; write the change to your own DB, drop an outbox record in the same transaction, publish it as an event. Migrate data with **dual-write + backfill**, then reconcile, then cut over.
5. **Only extract a module with a clean boundary.** If the payment module is tangled into the monolith's tables, fix that seam first. If the boundary isn't already clean, starting Strangler Fig just accelerates the disaster.

**Bottom line:** I'd set up weighted routing + monolith fallback at the gateway, validate the new Go service in shadow on mirrored traffic (not real money) first, then roll it out gradually. Don't forget the real work is the data split, not the routing: no shared tables, an anti-corruption layer, consistency via outbox/events. I unpack the architectural depth on sade.dev — don't try to peel off a module whose boundary isn't clean.

## Related Reading

- [When I Move to Microservices](https://sade.dev/en/journal/when-i-move-to-microservices) — sade.dev
- [For the message schema between Laravel and Go services, Protobuf or JSON Schema?](https://www.muhammetsafak.com.tr/en/just-ask/message-schema-between-laravel-and-go-protobuf-or-json-schema/) — Just Ask
- [Should I start a new project with microservices?](https://www.muhammetsafak.com.tr/en/just-ask/should-i-start-a-new-project-with-microservices/) — Just Ask
- [How do I set up distributed tracing (OpenTelemetry) across microservices?](https://www.muhammetsafak.com.tr/en/just-ask/distributed-tracing-with-opentelemetry-across-laravel-and-go/) — Just Ask
