Most product ambition in an established company runs into the same wall: the system that actually runs the business — the ledger, the policy engine, the order core — is the one thing you are not allowed to break, and usually not allowed to replace. It works, it is trusted, it is often decades of encoded rules, and the mandate is to build something new around it, not instead of it. Doing that well is an integration problem, and integration is where these initiatives quietly succeed or fail.
Start by protecting the new from the old
The first and most important pattern is the anti-corruption layer: a deliberate boundary that translates between the legacy core's model of the world and the clean model your new product wants to work with. The legacy system has its own vocabulary, its own quirks, its own assumptions baked in over years — and if those leak directly into your new code, the new product slowly becomes as tangled as the thing it was meant to modernize. The anti-corruption layer is the seam where the old language gets translated into the new one, and it is worth building even when it feels like overhead, because it is the difference between a product that stays clean and one that inherits every compromise of its host.
Choose how data moves, deliberately
Once the boundary exists, the real question is how data crosses it. There are three honest patterns and the right system usually uses more than one. You can call the legacy core synchronously through an API or gateway when the new product needs an answer right now and can tolerate the core's latency and availability — simple, but it couples your product's uptime to the core's. You can read from the core asynchronously by capturing its changes — change data capture off the database, or events the core emits — and building your own read models shaped for your product's needs; this decouples you from the core's performance and lets your product stay fast even when the core is slow. Or you can stream events as the integration backbone, so that new capabilities subscribe to what happens in the business rather than querying for it. The instinct to make everything a synchronous call is the most common mistake; it is also the one that turns a modern product into a fragile appendage of the legacy system.
The consistency questions you cannot skip
The moment data lives in two places — the legacy core and your product's read model — you have taken on the hardest problem in this kind of work: keeping them honestly in step. This is not a detail to solve later. You need clear answers to a few things from the start. When the core and your read model disagree, which one wins, and how does the other catch up? What happens when an event is delivered twice, or out of order, or not at all? How does a new capability behave when the core is briefly unavailable — does it fail, queue, or serve slightly stale data, and is that acceptable to the business? Making these choices explicitly, per capability, is the work. Idempotent handlers, a reconciliation process that continuously checks the two sides agree, and a conscious decision about acceptable staleness are what separate an integration that holds from one that generates a slow trickle of data-integrity incidents no one can quite explain.
Avoiding the distributed monolith
The failure mode that swallows these projects is subtle. You build your clean new services, you connect them to the core and to each other, and without noticing you create a web of synchronous calls where every request fans out to five systems and any one of them being slow makes the whole thing slow. You have not built a modern product on a legacy core; you have built a distributed monolith with worse failure characteristics than the monolith you started with. Avoiding it comes down to a few disciplines held consistently: prefer asynchronous integration over synchronous coupling wherever the business can tolerate it, let each capability own the data it needs rather than fetching it live from three places, and treat every synchronous dependency on the core as a deliberate cost you have chosen, not a convenience you reached for. The goal is a product that stays fast and available even when the old core is having a bad day — because the old core will have bad days, and the whole point was to stop letting them be your product's bad days too.