Java Omnibus
OOAD & Architecture / Microservices & Distributed Patterns / Microservices Anti-Patterns
Anti-Patterns Distributed Monolith Java Architecture
Microservices & Distributed Patterns

Most failed microservice programs are not too distributed. They are distributed in the wrong places and for the wrong reasons.

Microservices anti-patterns often start from good intentions: faster delivery, team autonomy, or cleaner scaling. The trouble appears when service boundaries are shallow, the data model is still shared, or every business flow requires synchronous hops across half the platform. Then the system becomes a distributed monolith with worse debugging.

Why This Topic Matters

Why it matters in real Java systems

If ordering, payments, inventory, and customer profiles all call each other synchronously for routine flows and still share database ownership, the platform has the cost of microservices without the benefit of independent change. Recognizing these failure signatures early is one of the highest-value microservices skills.

Core Explanation

The main design idea

The most common anti-pattern is premature distribution. Others include services defined by technical layers, shared databases masquerading as autonomy, tiny services that only forward requests, and central platform components that become hidden monoliths. Healthy microservices are not small for their own sake. They are cohesive, independently evolvable, and explicit about consistency boundaries.

Anti-pattern awareness is especially important because teams can keep adding containers and pipelines while the real architectural problem remains unchanged.

Concept Breakdown

Key ideas to keep in mind

Concept

Distributed monolith

Many services, one tangled dependency graph, no real autonomy.

Concept

Chatty workflow

Simple business requests require too many synchronous service hops.

Concept

Shared persistence

Multiple services still co-own the same database truths.

Common anti-pattern signatures

Common anti-pattern signatures

Anti-pattern What it looks like Healthier direction
Distributed monolithEvery deployment needs cross-team coordinationStrengthen boundaries or consolidate
Shared databaseServices query each other's tables directlyMove to database-per-service and published contracts
NanoservicesTiny services add network hops without ownership gainsMerge around cohesive capabilities
Chatty orchestrationRequest path fans across many dependenciesUse asynchronous collaboration or better capability design
Java Example

Java example in the Order Management domain

Java example: a suspicious chatty flow
package org.javaomnibus.checkout;

public final class CheckoutFacade {
    public Receipt place(CheckoutRequest request) {
        customerService.verify(request.customerId());
        pricingService.price(request.lines());
        inventoryService.check(request.lines());
        orderService.create(request);
        paymentService.authorize(request.payment());
        loyaltyService.apply(request.customerId());
        notificationService.sendConfirmation(request.customerId());
        return receiptService.fetchLatest(request.customerId());
    }
}
Code Walkthrough

Step by step

  1. The problem is not that many capabilities exist. The problem is that one synchronous request path depends on almost all of them.
  2. This shape becomes fragile under latency spikes, partial failure, and team coupling.
  3. The healthier answer may be capability consolidation, asynchronous events, or a narrower user-facing transaction boundary.
  4. Remote calls should represent meaningful collaboration, not accidental decomposition.
Real-World Usage

Where this helps in practice

  • Architecture reviews of service sprawl
  • Refactoring platforms that feel slower after moving to microservices
  • Teaching teams how to recognize a distributed monolith
Trade-Offs

Trade-offs and when to be careful

  • Avoiding anti-patterns sometimes means merging services, which can feel politically harder than adding more.
  • A service architecture may temporarily tolerate some anti-pattern symptoms during migration, but they should not become the target state.
Common Mistakes

Frequent mistakes to watch for

  • Assuming more services automatically means better architecture
  • Keeping shared tables while claiming service autonomy
  • Designing APIs around internal implementation details
  • Forgetting that network calls cost more than method calls in both latency and failure modes
Related Pages

Related concepts


What To Read Next