DEV Community

Saras Growth Space
Saras Growth Space

Posted on

LLD Domain Modeling: Final Anti-Pattern Guide (How Good Designs Slowly Turn Bad)

This is the final closure piece for Domain Modeling.

Because understanding “good design” is not enough.

You also need to understand:

how good designs fail over time.

Most real-world systems don’t collapse suddenly.

They degrade slowly through small modeling mistakes.


Anti-Pattern 1 — The God Object Evolution

It starts harmless:

UserService
OrderService
BookingService
Enter fullscreen mode Exit fullscreen mode

Then slowly:

  • more methods get added
  • unrelated logic gets inserted
  • workflows accumulate inside one class

Eventually:

OrderService does everything
Enter fullscreen mode Exit fullscreen mode

Symptoms:

  • impossible to test
  • impossible to understand
  • every change causes side effects

Root cause:

unclear responsibility boundaries


Anti-Pattern 2 — Anemic Domain Model

Entities look like:

class Order {
    String status;
    double amount;
}
Enter fullscreen mode Exit fullscreen mode

All logic moves to services.

Result:

  • entities become passive data holders
  • business rules scatter everywhere
  • duplication increases

Problem:

behavior is separated from state


Anti-Pattern 3 — Invariants Scattered Everywhere

Instead of one place:

Order must be valid only if payment is successful
Enter fullscreen mode Exit fullscreen mode

you find:

  • controllers checking rules
  • services repeating validations
  • helpers duplicating logic

Result:

  • inconsistent enforcement
  • bugs under edge cases

Root cause:

no central ownership of business rules


Anti-Pattern 4 — Wrong Aggregate Boundaries

Example:

Cart + Order + Payment mixed together
Enter fullscreen mode Exit fullscreen mode

This causes:

  • shared state confusion
  • inconsistent updates
  • coupling explosion

Correct idea:

each aggregate must protect one consistency boundary


Anti-Pattern 5 — Over-Splitting Services

Opposite problem:

Instead of clarity:

CartService
CartItemService
CartPricingService
CartValidationService
CartCouponService
Enter fullscreen mode Exit fullscreen mode

Now:

  • logic becomes fragmented
  • debugging becomes difficult
  • flow becomes unclear

Root cause:

splitting without thinking about ownership


Anti-Pattern 6 — Leaking Boundaries Across Contexts

Example:

  • Payment logic inside Order system
  • Inventory rules inside Cart system
  • Booking logic inside User system

This leads to:

  • tightly coupled systems
  • unpredictable changes
  • breaking changes across modules

Correct idea:

each bounded context must own its model fully


Anti-Pattern 7 — Primitive Obsession

Example:

String price
String location
String status
Enter fullscreen mode Exit fullscreen mode

Instead of:

  • Money
  • Location
  • State

This causes:

  • missing validations
  • scattered rules
  • inconsistent usage

Root cause:

ignoring value objects


Anti-Pattern 8 — Overengineering Early

Beginners often add:

  • factories everywhere
  • interfaces everywhere
  • abstract layers everywhere

Even before understanding domain.

Result:

  • unnecessary complexity
  • slower development
  • harder debugging

Rule:

complexity must be earned, not assumed


Anti-Pattern 9 — No State Modeling

Instead of:

CREATED → PAID → SHIPPED
Enter fullscreen mode Exit fullscreen mode

you see:

status = "anything"
Enter fullscreen mode Exit fullscreen mode

This leads to:

  • invalid transitions
  • inconsistent states
  • hidden bugs

Root cause:

missing lifecycle thinking


Anti-Pattern 10 — Mixing Intent and Truth

Example:

  • Cart (intent)
  • Order (truth)

When mixed:

  • pricing becomes inconsistent
  • checkout becomes unreliable
  • state confusion increases

Correct separation:

intent vs committed state


The Hidden Pattern Behind All Failures

Almost every design failure comes from one root cause:

lack of clear ownership of business behavior
Enter fullscreen mode Exit fullscreen mode

When ownership is unclear:

  • logic spreads
  • rules duplicate
  • states break
  • boundaries dissolve

How Strong Systems Avoid These Issues

Good systems consistently enforce:

  • clear entity responsibilities
  • centralized invariants
  • well-defined aggregates
  • strict bounded contexts
  • explicit state machines
  • minimal necessary services

Not because of theory.

But because:

they prevent long-term decay.


The Final Mental Model

Think of LLD like this:

If behavior has unclear ownership → design will degrade
If invariants are scattered → system will break
If boundaries are unclear → complexity will explode
Enter fullscreen mode Exit fullscreen mode

Good design is simply:

preventing these failures from happening.


The Most Important Insight

Domain modeling is not about making systems complex.

It is about:

preventing complexity from spreading uncontrollably.

Because in real systems:

  • code evolves
  • teams change
  • requirements grow
  • edge cases appear

And only systems with strong modeling foundations survive this evolution cleanly.

That is the real purpose of Domain Modeling in LLD.

Top comments (0)