Skip to main content

Anemic Domain Model

An Anemic Domain Model holds data with no behavior, pushing all logic into external services, which scatters rules and weakens encapsulation. Move behavior onto entities to build a rich, DDD-aligned model that enforces its own invariants, following Tell-Don't-Ask.

An Anemic Domain Model is a design in which domain objects contain data — fields with getters and setters — but almost no behavior. The business logic that should live with that data is instead pushed into a separate layer of service or manager classes that operate on the objects from the outside. Martin Fowler named it an anti-pattern because it looks object-oriented but is procedural underneath, sacrificing the core benefit of objects: bundling data with the behavior that maintains its integrity.

Why It Happens

Several common practices lead here. Layered architectures sometimes prescribe "dumb" data objects and "smart" services as a default, without considering whether logic belongs with the data. ORMs and serialization frameworks reward plain data classes, nudging developers to keep entities behavior-free. Transaction-script thinking — writing a procedure per use case — comes naturally and scatters rules into services. Teams unfamiliar with rich modeling may also fear "fat" entities. Each step seems orderly, but the result is a domain model that is just a set of data structures.

Why It Hurts

When behavior lives outside the data, the rules that protect an object's invariants are no longer enforced by the object itself, so any caller can put it into an invalid state. Logic scatters across many services, and the same rule tends to be re-implemented in several places, drifting out of sync. Encapsulation is weak because state is fully exposed through setters. Reasoning about an entity requires hunting through service classes to discover what can legally happen to it. The model communicates nothing about the domain; it is a passive record acted upon by procedures, which makes the codebase harder to understand and evolve as complexity grows.

Warning Signs

  • Entities consist almost entirely of getters and setters.
  • All meaningful logic sits in ...Service or ...Manager classes.
  • The same validation or calculation appears in multiple services.
  • Objects can be mutated into invalid states from outside.
  • The domain model reads like database rows, not domain concepts.

Better Alternatives

Build a rich domain model where entities and value objects own the behavior that operates on their data and enforce their own invariants. Apply Domain-Driven Design to align objects with domain concepts and keep rules close to the data they govern. Follow the Tell, Don't Ask principle: instead of pulling data out to act on it externally, tell the object to perform the operation, letting it protect its own state.

How to Refactor Out of It

Locate logic in service classes that primarily manipulates a single entity's data, and move it onto that entity as a method, replacing external state changes with intention-revealing operations. Make fields private and expose behavior rather than setters, so invariants are enforced in one place. Consolidate duplicated rules into the entity or a value object. Keep services for genuine cross-entity orchestration, but slim them down as behavior migrates into the model. Do this incrementally behind tests. The goal is a model where each object guarantees its own validity and clearly expresses what it can do, rather than a passive bag of data steered by external procedures.