Java Omnibus
OOAD & Architecture / Domain-Driven Design / Anemic vs Rich Domain Model
DDD Rich Domain Model Anemic Domain Model Java
Tactical DDD

The difference between an anemic and rich domain model is often the difference between a model that explains the business and one that merely stores data for services.

An anemic model carries data while real business behavior lives somewhere else. A rich model keeps meaningful rules close to the state they protect. DDD does not require every object to become clever, but it strongly prefers that the business model actually owns business meaning.

Why This Topic Matters

Why it matters in real Java systems

If order status rules, refund rules, and shipment constraints all live in services, the domain model becomes a passive schema. That makes the code look object-oriented while behaving procedurally. Richer models often improve cohesion and make business rules easier to trust.

Core Explanation

The main design idea

The real issue is not whether there are getters or services. The issue is where meaningful business decisions live. In an anemic model, entities often expose state while services perform all the important work. In a richer model, entities and value objects protect invariants and express behavior where it belongs.

DDD leans toward richer models when the domain is complex enough to justify them. But richness should be earned through real meaning, not through arbitrary method stuffing.

Concept Breakdown

Key ideas to keep in mind

Concept

Anemic model

Data structures at the center, procedural logic around them.

Concept

Rich model

Objects protect invariants and carry meaningful business behavior.

Concept

Main design test

Does the model explain the domain, or do services explain everything for it?

Refactoring direction

Refactoring direction

Typical move: if a service repeatedly validates or changes Order state, ask whether part of that logic should live inside Order itself. The goal is not zero services. The goal is honest behavior ownership.
Java Example

Java example in the Order Management domain

Java example: rich behavior inside the model
package org.javaomnibus.ecommerce.domain;

public final class Order {
    private OrderStatus status;

    public void markRefunded() {
        if (status != OrderStatus.PAID) {
            throw new IllegalStateException("Only paid orders can be refunded");
        }
        status = OrderStatus.REFUNDED;
    }
}
Code Walkthrough

Step by step

  1. The rule is enforced where the state lives.
  2. That reduces duplication and makes the behavior easier to trust.
  3. This is richer modeling without pretending every class must be complicated.
  4. DDD often improves most when these small moves accumulate consistently.
Real-World Usage

Where this helps in practice

  • Refactoring service-heavy enterprise Java code
  • Moving business invariants closer to the model
  • Teaching teams how object-oriented design and DDD reinforce each other
Trade-Offs

Trade-offs and when to be careful

  • Richer models improve meaning, but forcing every rule into one object can also create oversized aggregates or misplaced responsibilities.
  • The goal is better behavior placement, not maximal object cleverness.
Common Mistakes

Frequent mistakes to watch for

  • Equating richness with more methods regardless of meaning
  • Keeping all important rules in services because that feels procedurally simpler
  • Ignoring aggregate boundaries while trying to enrich objects
  • Calling a model rich when it still mainly exposes state for others to manipulate
Related Pages

Related concepts


What To Read Next