The Inheritance Problem

So you inherit a distributed system with components distributed early, databases fragmented, team churn erasing why boundaries exist, deployments coordinating across services and migrations, distributed complexity without distributed benefits.

A distributed monolith.

The Fog Problem

Aging systems can suffer from architectural amnesia. Boundaries reflect old org charts. Components couple through shared databases, common libraries, undocumented assumptions.

Standard advice: add abstraction layers or apply domain-driven design.

Abstraction over poorly understood systems is expensive. You build scaffolding around structures you haven’t examined, often abstracting over wrong boundaries and encoding coupling into new interfaces. This leads to the sinkhole anti-pattern: layers passing data without adding value, becoming maintenance burdens. Do you know where true boundaries are before committing?

DDD requires visibility you may lack: systems predating your team, fragmented databases with unclear ownership, knowledge living only in production incidents, missing Architecture Decision Records, team topologies shifted without architectural adjustment.

You can’t draw boundaries through fog or abstract over boundaries you don’t understand.

Strategic Consolidation

Consolidate services and databases with high cohesion and coupling. This creates clarity.

Reduces cognitive load. Consolidated systems eliminate the overhead of tracking distributed interactions.

Makes boundaries visible. Services calling through convoluted APIs or doing cross-database joins for simple queries reveal artificial boundaries.

Enables safe refactoring. Unified codebases support compiler checks, transactional migrations, atomic commits versus coordinating changes across services, pipelines, teams, schemas.

Reverses Conway’s Law. Reshape architecture independent of obsolete team structures.

How to Consolidate

Identify candidates through temporal coupling; services changing together in version history are one logical component. Measure coupling (inter-service calls, cross-database queries), cohesion (related data and functions), and database relationships (shared tables, replication).

Consolidate selectively. Merge high-cohesion, high-coupling components. Keep independent services separate. Database consolidation eliminates network calls for joins and makes relationships explicit.

Enforce internal boundaries. Use modular monolith patterns with clear interfaces. Document decisions in ADRs: why consolidated, what metrics drove it, intended boundaries, conditions for future extraction.

Align teams with topology. Three teams on one service might need three modules or different team structure.

Extract when justified. Different scaling needs, release cycles, genuine independence, bounded contexts with minimal data sharing.

Prevention

Architecture Decision Records preserve rationale across team changes. Team topologies aligned with domain boundaries prevent arbitrary splits. Systems with these practices rarely need aggressive consolidation.

The Goal

“Monolith first” works for greenfield. “Consolidate to rediscover boundaries” works for inherited complexity.

Not universal. Some systems benefit from current distribution. Some teams can refactor in place. For many facing unclear boundaries and knowledge loss, consolidation provides a pragmatic path to appropriate boundaries informed by reality: measured coupling and cohesion, aligned topologies, documented rationale.