Data without behavior
The domain objects exist, but they do not own the rules that give their data meaning.
An anemic domain model is one of the most common anti-patterns in enterprise Java. The entities exist, but they mostly expose fields and accessors. The real rules live in services, utilities, controllers, or repositories. This makes the model look object-oriented on the surface while behaving procedurally underneath.
In an order-management system, Order, Payment, and Shipment should usually protect at least some important rules and state transitions. When they become passive records, the business logic starts scattering into service layers, and the domain becomes harder to understand and defend.
An anemic model is often the result of convenience or framework defaults. It feels easy to persist entities and keep behavior in services, especially when teams are moving quickly. The long-term cost is that the domain loses its voice. Objects no longer express business meaning directly.
For Java teams, this is especially important because anemic models can coexist with sophisticated frameworks and still degrade design quality. The presence of entities and repositories does not guarantee real OO design.
The fix is not to force all behavior into entities. The fix is to restore meaningful domain ownership where the object should naturally protect state and rules.
The domain objects exist, but they do not own the rules that give their data meaning.
Service classes start simulating object behavior by manipulating passive entities from the outside.
If the object does not defend its own state transitions, every caller must remember the rules separately.
package org.javaomnibus.ecommerce.antipatterns;
public final class Order {
private String status;
private String customerId;
private double total;
public String getStatus() { return status; }
public void setStatus(String status) { this.status = status; }
public String getCustomerId() { return customerId; }
public void setCustomerId(String customerId) { this.customerId = customerId; }
public double getTotal() { return total; }
public void setTotal(double total) { this.total = total; }
}
public final class OrderPaymentService {
public void markPaid(Order order) {
if (!"DRAFT".equals(order.getStatus())) {
throw new IllegalStateException("Only draft orders can be paid");
}
order.setStatus("PAID");
}
}
Not every data structure should be rich. Some boundaries are intentionally simple. The anti-pattern is not about having any simple objects. It is about weakening core domain concepts that should own business meaning.