Your objects have no behavior.
TL;DR: Don't use objects as data structures
Problems
Solutions
1) Find Responsibilities.
2) Protect your attributes.
3) Hide implementations.
4) Follow Tell-Don't-Ask principle
Refactorings ⚙️
https://maximilianocontieri.com/refactoring-016-build-with-the-essence
https://maximilianocontieri.com/refactoring-009-protect-public-attributes
https://maximilianocontieri.com/refactoring-001-remove-setters
Examples
Context
If you let your objects become data buckets, you kill the connection between your logic and your language.
Anemic models are classes that contain only data (properties) with little or no behavior.
They're essentially glorified data structures with getters and setters.
When you create anemic models, you end up putting all the logic that should live in these objects into service classes instead duplicated the logic across multiple services.
This approach breaks object-oriented principles by separating data from the behavior that manipulates it.
You'll find yourself writing procedural code that pulls data out of objects, performs operations on it, and then pushes the results back in.
This creates tight coupling between your services and objects, making your codebase harder to maintain and evolve.
When you identify an anemic model in your code, it's a sign that you're missing opportunities for better encapsulation and more intuitive object design.
Rich domain models lead to code that's more maintainable, testable, and closer to how you think about the problem domain.
Sample Code
Wrong ❌
[Gist Url]: # (https://gist.github.com/mcsee/73f84d80f7c3e89a216dd9e40ab71bcc)
public class Song {
String name;
String authorName;
String albumName;
}
Right
[Gist Url]: # (https://gist.github.com/mcsee/78f2dd78120db843c960ed41839f29cb)
public class Song {
private String name;
private Artist author; // Will reference rich objects
private Album album; // instead of primitive data types
public String albumName() {
return album.name() ;
}
Detection
[X] Semi-Automatic
Sophisticated linters can automate detection.
They should ignore setters and getters and count real behavior methods.
Level
[X] Beginner
Why the Bijection Is Important ️
If we ask a domain expert to describe an entity he/she would hardly tell it is 'a bunch of attributes'.
The power of object-oriented programming comes from modeling real-world concepts directly in code.
When you create anemic models, you break the bijection between the domain and your code.
AI Generation
AI code generators often produce anemic models because they follow common but flawed patterns found in many codebases.
When you ask an AI to generate a basic model class, it will typically create a class with properties and getters/setters but no behavior.
This perpetuates the anemic model anti-pattern.
You need to specifically instruct AI tools to generate rich domain models with behavior, not just data holders.
Be explicit in your prompts about including relevant methods that encapsulate business logic within the model.
AI Detection
AI tools can help identify anemic models with simple instructions like "find classes with many getters/setters but few business methods" or "identify service classes that should be refactored into domain models."
Determining which behavior truly belongs in a model requires domain knowledge and design judgment that current AI tools lack.
AI can flag potential issues, but you still need to make the final decision about where behavior belongs.
Try Them!
Remember: AI Assistants make lots of mistakes
Suggested Prompt: Convert the anemic object into a rich one focusing on behavior instead of structure
Conclusion
Anemic models might seem convenient at first, but they lead to scattered logic, poor encapsulation, and maintenance headaches.
Senior developers create rich domain models focusing on their behavior.
By moving logic from services into models, you create code that's more intuitive, maintainable, and aligned with object-oriented principles.
Your objects should do things, not just store data.
Avoid anemic models. Focus always on protocol instead of data.
behavior is essential, data is accidental.
Relations ❤️
https://maximilianocontieri.com/code-smell-28-setters
https://maximilianocontieri.com/code-smell-15-missed-preconditions-1
https://maximilianocontieri.com/code-smell-210-dynamic-properties
https://en.wikipedia.org/wiki/Anemic_domain_model
https://refactoring.guru/es/smells/data-class
https://maximilianocontieri.com/nude-models-part-i-setters
https://maximilianocontieri.com/nude-models-part-ii-getters
https://maximilianocontieri.com/how-to-decouple-a-legacy-system
Also Known as
Disclaimer
Code Smells are my opinion.
Credits
Photo by Stacey Vandergriff on Unsplash
Object-oriented programming increases the value of these metrics by managing this complexity. The most effective tool available for dealing with complexity is abstraction. Many types of abstraction can be used, but encapsulation is the main form of abstraction by which complexity is managed in object-oriented programming.
Rebecca Wirfs-Brock
https://maximilianocontieri.com/software-engineering-great-quotes
This article is part of the CodeSmell Series.
https://maximilianocontieri.com/how-to-find-the-stinky-parts-of-your-code