⏱ 21 min read
There is a moment in the life of many microservice estates when the architecture drawing stops being a map and starts being a warning label. microservices architecture diagrams
At first, the topology looked sensible. A few services. Clear APIs. Maybe an event bus in the middle. Teams moved quickly, everyone talked about autonomy, and the slideware looked modern enough to impress leadership and terrify auditors. Then the system grew up. New domains appeared. Old domains leaked. One service became the place where “just one more rule” went to die. Kafka topics multiplied like weeds in a neglected garden. Synchronous calls piled on top of asynchronous flows. Suddenly no one could answer a simple question such as: _where does customer status really get decided?_ event-driven architecture patterns
That is the point where topology refactoring stops being an engineering cleanup and becomes a business survival skill.
Service topology refactoring is not about redrawing boxes because the current diagram looks ugly. Ugly diagrams are often honest diagrams. Topology refactoring is about changing the interaction shape of a microservice system so domain semantics become clearer, operational behavior becomes safer, and change becomes cheaper again. The diagram matters because the diagram reveals power: who owns decisions, who depends on whom, where coupling hides, and where failure spreads.
The mistake many organizations make is to treat topology as plumbing. It isn’t. Topology is encoded organizational intent. If your domain boundaries are confused, your topology will betray you. If your migration strategy is naïve, your topology will punish you. And if your teams keep publishing “integration events” that are really remote procedure calls wearing Kafka costumes, your topology will eventually become an expensive distributed monolith.
This article goes deep into how to refactor service topology in a microservices estate, why it matters, how to migrate without setting the business on fire, and when not to do it at all.
Context
Microservices gave us a useful discipline: align software around business capabilities and let teams evolve independently. That promise still holds, but only if the topology reflects the domain.
In a healthy architecture, topology follows semantics. Order decisions live near the order domain. Pricing decisions live near pricing. Inventory allocation lives where stock ownership and reservation logic are understood. Events carry meaningful state transitions, not random row changes from a database that no one should have exposed in the first place.
Over time, though, enterprises drift.
A CRM service starts making credit decisions because it already had the customer data. An order orchestration service begins calculating promotions because “it was easiest there.” A legacy ERP integration service becomes the canonical source for product availability because everyone trusts its batch exports more than the newer systems. Kafka topics become unofficial contracts with poor stewardship. Reporting consumers depend on event fields that were never intended to be stable. A supposedly autonomous service cannot answer a request without making six synchronous calls and waiting on two downstream caches.
This is not rare. It is the normal aging pattern of enterprise systems.
Topology refactoring is the disciplined act of correcting that drift. Sometimes that means splitting a service that owns conflicting business rules. Sometimes it means collapsing overly fine-grained services into a larger domain service. Sometimes it means moving from deep synchronous chains to event-driven propagation. Sometimes it means introducing a process manager or saga because business transactions span multiple bounded contexts. Sometimes it means deleting Kafka from places where teams used it as an excuse not to make ownership decisions.
In other words, topology refactoring is not one pattern. It is a category of architectural change guided by domain-driven design, operational evidence, and migration pragmatism.
Problem
Most service topologies fail for ordinary reasons, not exotic ones.
The first problem is domain ambiguity. Teams cannot agree on what a service actually owns. “Customer,” for example, often hides three different concepts: identity, account relationship, and commercial profile. When one service owns all three, every downstream system depends on it for unrelated reasons. When three services own overlapping versions without explicit contracts, reconciliation becomes a permanent tax.
The second problem is interaction sprawl. Too many synchronous dependencies create long latency chains and broad failure blast radius. Too many asynchronous dependencies create eventual consistency confusion, duplicate workflows, and inscrutable debugging. The issue is not sync versus async. The issue is topology without intent.
The third problem is integration masquerading as domain design. Enterprises often expose legacy shapes into the microservice estate. If the ERP says product, every new system acts as though “product” is a clean concept. It rarely is. Product catalog, sellable SKU, fulfillment item, pricing item, and compliance-controlled item are often different bounded contexts pretending to be one thing.
The fourth problem is topology calcification. Once producers and consumers rely on a service interaction pattern, change gets expensive. Kafka can make this worse when topics become public utility lines with no stewardship. You end up with dozens of consumers, all making assumptions about ordering, retention, schema evolution, and replay behavior. Refactoring the topology then requires not only code change but social negotiation.
The final problem is operational illusion. Teams think they have independent services, but incidents tell a different story. One degraded dependency and half the business flow stalls. That is not a microservice architecture. That is a distributed outage generator.
Forces
Topology refactoring sits in the middle of competing forces. Ignore them and you produce architecture theater.
Domain coherence versus delivery speed
The cleanest bounded contexts are rarely the fastest path for a quarter-end deadline. Teams compromise. They centralize logic in the nearest service. They publish broadly useful events without deciding whether they are domain events, integration events, or CDC artifacts. Refactoring later is expensive but often necessary.
Autonomy versus consistency
Every enterprise wants autonomous teams. Every business also wants a single version of key facts. Those goals collide. Customer limits, inventory positions, and financial balances are not casual data products. They are controlled decisions. If ownership is vague, consistency becomes everyone’s problem and therefore nobody’s problem.
Synchronous correctness versus asynchronous resilience
A blocking API call can provide immediate confirmation. It can also drag your critical path across six services. Event-driven designs reduce direct coupling and improve resilience, but they introduce delayed visibility, retries, reconciliation, deduplication, and state convergence concerns. Refactoring topology often means deciding where the business truly needs immediate consistency and where “consistent enough, soon enough” is acceptable.
Local optimization versus whole-flow clarity
Teams optimize their service in isolation. The business cares about end-to-end outcomes: quote-to-cash, claim adjudication, order-to-fulfillment. Topology refactoring often reveals that local service boundaries are reasonable but the flow between them is not.
Legacy gravity
The old systems are not going away on your preferred timeline. ERP, mainframe, packaged SaaS, data warehouse, MDM, and partner gateways all exert gravity. Good topology refactoring acknowledges this and designs seams. Bad topology refactoring pretends the world is greenfield and collapses on first contact with reality.
Solution
The core solution is simple to say and hard to do:
Refactor service topology around decision ownership, not data possession.
That is the heart of the matter.
A service should not exist because it stores a table or exposes a CRUD API. A service should exist because it owns business decisions within a bounded context. Once you accept that, topology refactoring becomes a series of practical moves:
- Identify domain decisions and bounded contexts.
- Map current interactions to those decisions.
- Separate command paths from propagation paths.
- Reduce synchronous dependency chains in critical flows.
- Use Kafka or other event platforms for state propagation and domain events, not as a substitute for ownership.
- Introduce reconciliation where eventual consistency is unavoidable.
- Migrate progressively using strangler patterns, anti-corruption layers, and dual-running where necessary.
The best topology is usually not the one with the most services. It is the one where people can answer, without hesitation:
- who decides,
- who publishes that decision,
- who consumes it,
- what happens when they disagree,
- how the system heals when messages are delayed or wrong.
That last point matters. In real enterprises, reconciliation is not a side feature. It is part of the architecture. If an order is accepted before credit confirmation arrives, you need a formal mechanism to resolve mismatches. If inventory reservations race with warehouse updates, you need compensating logic and auditability. If Kafka consumers fall behind, you need replay and correction behavior that the business can tolerate.
A topology that assumes perfect propagation is fantasy. A topology that plans for divergence is architecture.
Architecture
A common target shape is to move from a service mesh of incidental calls toward a topology with clearer domain cores, explicit workflow coordination, and event-driven state distribution.
Before refactoring
This kind of topology is common. The Order Service has become the center of the universe. It orchestrates too much. It depends on too many decisions it does not own. Other services call around it. Legacy adapters leak authority. Kafka exists, but mostly as exhaust.
This shape has familiar symptoms:
- long synchronous critical path
- inconsistent decision ownership
- duplicated business rules
- event streams that reflect internal implementation rather than domain events
- hard incident diagnosis because every flow is entangled
After refactoring
The point here is not to make everything asynchronous. Quite the opposite. Command paths remain explicit where the business needs an authoritative answer: price this order, allocate stock, approve credit. But the propagation of resulting state is event-driven. That reduces broad query dependency and allows downstream services to react without inserting themselves into the transaction path.
This is where domain-driven design earns its keep. Pricing, inventory allocation, and credit are not just technical services. They are bounded contexts with different models, rules, and temporal behavior. An “order accepted” event means something different from “inventory reserved” or “credit approved.” Refactoring topology means respecting those semantics instead of flattening everything into generic JSON blobs.
Domain semantics matter more than nouns
Many poor architectures use shared nouns as service boundaries. Customer Service. Product Service. Order Service. Fine names, weak design.
A better approach is to ask:
- What decisions are made here?
- What invariants must hold here?
- What timelines matter here?
- What language does the business actually use?
For example, “inventory” often hides at least three concepts:
- available-to-promise
- reservable stock
- physical warehouse position
If a single Inventory Service owns all of that and every team depends on it synchronously, the topology will choke. Better to make allocation decisions explicit and publish state transitions for other contexts to consume.
Add reconciliation as a first-class component
This diagram shows the part many whitepapers skip: systems disagree. Messages arrive late. Consumers fail. A reservation succeeds after the customer’s credit has already been rejected. A duplicate event sneaks in due to retry. A consumer replays history and temporarily rebuilds stale state. You need a reconciliation and exception handling capability to detect divergence, route cases, and trigger compensating actions.
That is not an implementation detail. It is how distributed business systems remain trustworthy.
Migration Strategy
You do not refactor service topology with a big-bang rewrite unless you enjoy public failure.
The practical approach is a progressive strangler migration. Carve seams, redirect behavior incrementally, and keep the business running while the topology changes under load.
1. Start with decision mapping
Before writing code, identify the business decisions in the current topology. Which service is really deciding price? Which one is truly authoritative for credit approval? Where does order acceptance become official? Which events are relied on downstream, whether or not they were intended as contracts?
This exercise often reveals that the current service names are misleading. Good. Better to discover semantic mess in a workshop than during a quarter-close outage.
2. Create anti-corruption layers at legacy boundaries
Do not let ERP or mainframe schemas define the new topology. Introduce an anti-corruption layer that translates between legacy models and your bounded contexts. If the legacy system calls something a “customer,” but your domains need party, account, and billing entity separated, that translation belongs at the boundary.
This is not glamorous work. It is essential work.
3. Separate command APIs from event publication
A useful migration move is to keep current command entry points stable while changing what happens behind them. For example, Order Service can continue receiving order submissions, but delegate pricing, inventory, and credit decisions to emerging bounded contexts. The old broad synchronous fan-out gets narrowed and clarified.
At the same time, start publishing domain events from the new contexts into Kafka with proper schema management and stewardship.
4. Dual run and compare
When moving a decision from one place to another, run both for a period and compare results. This is especially important for pricing, eligibility, fraud, and allocation. Differences should be expected. The point is to understand them before cutover.
This is also where reconciliation starts paying off. If the new topology can detect semantic divergence early, migration risk drops dramatically.
5. Strangle read dependencies
Consumers often keep old services alive longer than command paths do. Reporting systems, portals, notifications, and partner integrations all rely on read models. Rather than forcing all of them to call the new domains directly, publish stable events and build fit-for-purpose projections. Let consumers migrate gradually.
6. Cut by business slice, not technical layer
Refactor one coherent flow at a time: order submission for one region, returns processing for one channel, claims adjudication for one product line. Enterprises that migrate by technical layer alone usually create months of hybrid complexity with little business value.
7. Keep rollback realistic
A migration plan without rollback is optimism pretending to be architecture. If a new context starts producing inconsistent outcomes, can traffic be redirected? Can events be quarantined? Can consumers tolerate schema fallback? Design rollback before the launch meeting, not during the incident bridge.
Enterprise Example
Consider a global retailer modernizing its order-to-fulfillment estate.
The starting point looked familiar. There was a central Order Service receiving traffic from e-commerce, store kiosks, and call center applications. It synchronously called Pricing, Inventory, Customer, and Payment services. It also published Kafka events after the fact. Meanwhile, the ERP adapter published inventory updates from nightly and intraday feeds. Teams said they had microservices. Incidents said otherwise.
The real issue was semantic confusion.
“Inventory” meant warehouse stock for logistics, available-to-sell for commerce, and promised allocation for orders. The same term, three different meanings. The Pricing Service also applied customer-specific discounts that really belonged to contract pricing rules in another domain. Customer status was sourced from CRM, but credit holds came from finance. The Order Service had grown into a policy blender.
During peak seasonal events, a delay in the ERP adapter caused stale stock visibility. Order submission latency spiked because the central service was waiting on too many decisions. Some orders were accepted and later cancelled because inventory was not actually reservable. Others were blocked unnecessarily because credit data arrived late. Operations teams spent hours reconciling mismatches.
The refactoring strategy did not begin with rewriting Order Service. It began with redefining decision ownership.
- Pricing Context owned sell price determination.
- Credit Decision Context owned commercial approval for order release.
- Inventory Allocation Context owned promise and reservation logic.
- Order Management owned the lifecycle of customer orders and policy for what states were acceptable at each stage.
- Legacy ERP remained system-of-record for warehouse execution and financial posting, but lost authority over near-real-time commerce decisions.
Kafka became the propagation backbone, not the decision engine. Order Management issued commands where immediate answers mattered. Resulting domain events flowed into Kafka: PriceDetermined, InventoryReserved, CreditApproved, OrderAccepted, AllocationFailed.
A reconciliation service monitored orphaned or contradictory states. For example:
- order accepted but no reservation within SLA
- credit approved after order cancellation
- duplicate reservation event for same line item
- stale inventory update overwriting a newer allocation state
The migration was done region by region. For one region, inventory allocation moved first because that was the largest source of cancellations. The old ERP-derived availability feed was preserved as a fallback reference, but reservation decisions moved to the new context. During dual run, the team found that the old logic included undocumented safety stock behavior in one distribution center. Good architecture work often looks like archaeology.
Once inventory was stabilized, pricing moved next. A major lesson emerged: several downstream consumers relied on old order events carrying embedded pricing fields. Those fields had never been designed as a durable contract, but they had become one in practice. The team introduced versioned event schemas and projection topics for compatibility. This avoided breaking analytics and customer communication systems.
The business results were not magical, but they were meaningful:
- lower synchronous latency during checkout
- fewer false accepts followed by cancellation
- clearer ownership of order exceptions
- faster onboarding of a new sales channel because topology was easier to understand
- incident resolution based on explicit state transitions rather than distributed guesswork
That is what good topology refactoring buys you. Not architectural purity. Operational leverage.
Operational Considerations
A refactored topology only earns its keep if it behaves well under production stress.
Observability must follow business flows
Tracing HTTP calls is not enough. In an event-driven topology, you need correlation across commands, events, compensations, and reconciliations. A business identifier such as order ID, claim ID, or shipment ID should be traceable end to end across Kafka topics and service logs.
Schema governance matters
If Kafka is part of the topology, treat event schemas as contracts. Version them. Classify them. Distinguish domain events from integration events and CDC streams. Otherwise, consumers will build on accidental structure and topology refactoring will slow to a crawl.
Idempotency is table stakes
Duplicates happen. Retries happen. Replays happen. Consumers and side-effecting handlers must be idempotent or topology refactoring will produce strange, expensive bugs.
Backpressure and lag are business issues
Consumer lag is not merely a platform metric. It means delayed business truth. If inventory confirmations are lagging fifteen minutes behind order intake, commerce decisions may now be wrong. Operational dashboards should connect technical lag to business risk.
Reconciliation needs ownership
Do not build a reconciliation service and then leave no team accountable for its rules and queues. Exception handling becomes the shadow process of distributed systems. Own it explicitly or it will own you informally.
Data retention and replay strategy must be deliberate
Kafka replay is powerful, but replaying old events into consumers with side effects can recreate history in unsafe ways. Use event versioning, quarantine topics, and replay-safe projections. A topology that cannot be safely replayed is brittle.
Tradeoffs
Topology refactoring is valuable, but it is not free.
The first tradeoff is clarity versus complexity. A better domain topology often introduces more explicit components: process managers, anti-corruption layers, reconciliation flows, projection services. The architecture becomes clearer semantically while becoming more sophisticated operationally.
The second tradeoff is reduced runtime coupling versus increased eventual consistency. Removing synchronous dependencies shortens critical paths, but now state converges over time. The business must accept and understand that. Some domains can. Some cannot.
The third tradeoff is local service simplicity versus system-wide correctness. A service that emits events and lets others react may look elegant, but if no one owns end-to-end correctness the topology becomes a blame graph. Refactoring must preserve clear authority.
The fourth tradeoff is faster future change versus slower migration now. During refactoring, teams carry transition cost: dual writes, compatibility layers, schema versions, extra monitoring. Leaders must understand that architecture debt is paid in installments, not motivational speeches.
Failure Modes
There are several predictable ways this work goes wrong.
Refactoring the diagram, not the ownership
You move boxes around, rename services, add Kafka, and keep the same decision ambiguity. This is the most common failure. Nothing really changes except the slide deck.
Over-fragmentation
Teams split services too aggressively in pursuit of purity. What should have been one cohesive bounded context becomes a constellation of tiny services with constant chatter. If every business operation requires cross-service coordination, you have not improved the topology. You have aerosolized it.
Event theater
Every state change becomes an event. Topics multiply. Consumers subscribe eagerly. No one curates semantics. You gain decoupling and lose comprehension.
Hidden shared database habits
Two services still rely on the same underlying tables or synchronized schemas, so the topology claims autonomy while data reality keeps them coupled. This is a classic enterprise trap.
Missing reconciliation
The architecture assumes happy-path eventual consistency. Then a delay, duplicate, or out-of-order event appears and the business discovers that no one defined what “correct” means after divergence.
Legacy bypass
A team, under delivery pressure, bypasses the anti-corruption layer and consumes legacy structures directly “for now.” Congratulations. You have just imported yesterday’s model into tomorrow’s topology.
When Not To Use
Topology refactoring is not a universal remedy.
Do not do it when the domain is still volatile and poorly understood. If the business itself cannot yet articulate stable semantics, large topology changes will fossilize confusion.
Do not do it when the current problem is simply poor engineering hygiene. If services are healthy bounded contexts but code quality, test discipline, or observability is weak, fix those first. Topology change will not rescue sloppy delivery.
Do not force event-driven topology where the domain requires strong, immediate consistency across a narrow boundary and the throughput profile is manageable. A modular monolith or a small number of well-factored services may be the better answer.
Do not use Kafka because the organization likes the idea of streaming. Use it where asynchronous propagation, decoupled consumption, and replayable event history actually solve a problem. Kafka is a powerful tool. It is also an efficient way to institutionalize confusion if semantics are weak.
And do not start topology refactoring if leadership expects visible business results in two weeks but will not fund migration scaffolding, compatibility, and operational hardening. Refactoring topology is surgery, not cosmetics.
Related Patterns
Several patterns commonly accompany topology refactoring.
- Strangler Fig Pattern for progressive migration of legacy or oversized services.
- Anti-Corruption Layer to preserve domain integrity at legacy and external boundaries.
- Saga / Process Manager for coordinating long-running business workflows across bounded contexts.
- CQRS when command responsibility and read distribution have materially different shapes.
- Outbox Pattern for reliable event publication from transactional updates.
- Event Carried State Transfer for read-side propagation, used carefully.
- Domain Events for meaningful business transitions.
- Reconciliation / Exception Management for divergence detection and correction.
- Consumer-Driven Contracts where event and API consumers materially influence contract evolution.
These are tools, not ornaments. Use them because the domain and migration require them, not because they make the architecture look sophisticated.
Summary
Service topology refactoring in microservices is not about making diagrams cleaner. It is about restoring the relationship between architecture and business meaning.
When topology drifts, the signs are familiar: ambiguous service ownership, overgrown synchronous chains, Kafka used as institutional exhaust, legacy models leaking everywhere, and operational incidents that reveal hidden coupling. The cure is not “more microservices.” The cure is to refactor around bounded contexts and decision ownership.
That means being blunt about domain semantics. It means distinguishing commands from events. It means reducing runtime coupling where resilience matters, while preserving synchronous authority where the business truly needs immediate answers. It means planning for reconciliation because distributed systems disagree in production. It means migrating progressively with strangler techniques, anti-corruption layers, dual running, and rollback paths that are real.
The memorable line here is simple: the topology is the truth serum of your architecture. It exposes what your organization really believes about ownership, authority, and change.
If you refactor that topology with domain-driven discipline and enterprise realism, the result is not just a better diagram. It is a system that fails more honestly, evolves more cheaply, and makes business decisions in the right place.
And in enterprise architecture, that is about as close to elegance as we get.
Frequently Asked Questions
What is a service mesh?
A service mesh is an infrastructure layer managing service-to-service communication. It provides mutual TLS, load balancing, circuit breaking, retries, and observability without each service implementing these capabilities. Istio and Linkerd are common implementations.
How do you document microservices architecture for governance?
Use ArchiMate Application Cooperation diagrams for the service landscape, UML Component diagrams for internal structure, UML Sequence diagrams for key flows, and UML Deployment diagrams for Kubernetes topology. All views can coexist in Sparx EA with full traceability.
What is the difference between choreography and orchestration in microservices?
Choreography has services react to events independently — no central coordinator. Orchestration uses a central workflow engine that calls services in sequence. Choreography scales better but is harder to debug; orchestration is easier to reason about but creates a central coupling point.