Skip to content

Data Class

Flag an object-oriented class that holds fields with getters and setters but no domain methods as a symptom that its behavior migrated outward into consumers, then resolve the verdict with one role question — should this class enforce its own invariants?

Core Idea

A data class is an object-oriented code smell: a class holds fields and exposes them via getters and setters but contains no methods performing meaningful domain operations. The behavior that belongs to it has migrated outward into its consumers, violating encapsulation — data and its operations should travel together. The class becomes a passive record, a struct wearing object syntax, with invariants enforced nowhere and validation duplicated across each consumer.

How would you explain it like I'm…

The Lunchbox Without Rules

Imagine a lunchbox that should know its own rule, like "no melted ice cream inside." Instead the lunchbox just holds food, and every kid who opens it has to remember the rule on their own, and each one does it a little differently. That is a data class. If a box is only ever meant to hold things and has no rules, that is fine; the problem is a box that should have rules but does not.

Info Box With No Rules

In some programming, a "class" is supposed to hold information and also do the jobs related to that information, like a bank account that checks you can't take out more money than you have. A data class is a class that only holds the information and lets everyone else read and change it. The jobs end up spread across all the other code that uses it, and each piece does them a little differently, so things get messy and mistakes creep in. Programmers call this a "code smell," a warning sign. The fix is to move those jobs back into the class. But a class that is meant only to carry information, with no rules to protect, is not a problem at all.

Behavior-Less Entity Smell

In object-oriented programming, encapsulation means a class bundles its data together with the operations on that data and enforces its own rules, or invariants. A data class is a code smell where a class has fields plus getters and setters but no real behavior; the logic that belongs to it has drifted into the classes that use it. As a result, every consumer reaches into the fields, validation is duplicated in slightly different ways, and the same data can end up in inconsistent states. Changing the data layout means hunting down every consumer. The usual fix, from Martin Fowler's Refactoring, is Move Method: pull the scattered behavior back into the class. The key test is whether the class should be enforcing rules; if it is a deliberate plain record, like a Python @dataclass or Java record, the same shape is intentional, not a smell.

 

A data class, in the code-smell sense, is a class that holds fields exposed through getters and setters but has no methods performing meaningful domain operations; the behavior that belongs with its data has migrated into the consumers. This violates encapsulation, the principle that a class should bundle data with the operations on it and enforce its own invariants. Consequences grow with the number of consumers: changes to the data layout ripple into every class that reached into the fields, and validation is duplicated inconsistently, so the same data can end up in different states depending on who touched it last. Martin Fowler catalogs it as a smell in Refactoring, with Move Method as the canonical remedy: relocate the behavior into the class until its behavioral surface matches its data. The diagnostic question is whether the class should enforce invariants. If it represents a domain entity with constraints, state transitions or business rules, it is anemic and behavior should move in; if it is an intentional pure-data record, it is a legitimate value object, and features like Java record, Kotlin data class, C# record struct and Python @dataclass exist to declare that intent.

Scope of Application

The smell lives within object-oriented software design, across subfields that share encapsulation as their explicit organizing principle.

  • Object-oriented design proper — beans with only getters and setters, the everyday review target.
  • Layered enterprise architecture — the same condition appears as the "Anaemic Domain Model" critique.
  • Domain-Driven Design — a central concern, with rich invariant-owning aggregates as the contrasting target.
  • Refactoring practice — a catalogued Fowler smell beside Feature Envy and Shotgun Surgery, fixed by Move Method.
  • Modern value-object features — Java record, Kotlin data class, Python @dataclass legitimize the no-behavior case.

Clarity

Naming the smell makes a hidden verdict legible: two classes with the same syntactic surface can warrant opposite judgments, and the name forces the reviewer to ask the one question the syntax cannot answer — should this class enforce invariants on its own data? It relocates the problem, reframing "this class is too simple" into "where did this class's behavior go, and is it now scattered and inconsistent?"

Manages Complexity

A large codebase presents an open-ended worry: somewhere behavior has drifted from its data and validation is diverging. The smell compresses that system-wide audit to a single recognizable local shape — an accessor-only class — then collapses the verdict to one binary parameter: should it enforce its own invariants? Every downstream consequence is read off that answer rather than re-derived per class.

Abstract Reasoning

The smell licenses a diagnostic move — reading a thin class as the symptom of behavior that migrated offstage. It does boundary-drawing — resolving a syntactically-undecidable verdict (value object versus anaemic entity) with one role question. It supports an interventionist move — routing to Move Method or record-formalization by that answer. And it is predictive — reading change-cost and severity off the consumer count.

Knowledge Transfer

Within object-oriented software the smell transfers as mechanism: the diagnostic, the verdict branch, and the remedies carry intact across enterprise architecture, DDD, and refactoring practice, all sharing encapsulation as an explicit commitment. Beyond the OO paradigm it does not transfer even as a failure — in functional programming and the relational model, separating data from operations is the correct default. What genuinely recurs is the parent structure — the absence of encapsulation and the resulting Coupling — not the named code smell.

Relationships to Other Abstractions

Local relationship map for Data ClassParents appear above the current abstraction, mutual partners to the right, and children below. Node labels state whether each abstraction is prime or domain-specific; colors identify relation types.Data ClassDOMAINPrime abstraction: Coupling — includesCouplingPRIMEPrime abstraction: Information Hiding — presupposesInformationHidingPRIMEDomain-specific abstraction: Code Smell — is a kind ofCode SmellDOMAIN

Current abstraction Data Class Domain-specific

Parents (3) — outgoing typed relations

  • Data Class is a kind of Code Smell Domain-specific

    Data Class is the code-smell species whose passive fields and accessors provide defeasible evidence that behavior and invariant enforcement migrated into consumers.

  • Data Class includes Coupling Prime

    Cross-class coupling is a constituent of the data-class smell because consumers reach into exposed state and carry the displaced behavior.

  • Data Class presupposes Information Hiding Prime

    The data-class smell presupposes information hiding because its verdict is that an entity exposes representation and invariants that its behavioral surface was expected to conceal.

Hierarchy paths (7) — routes to 7 parentless roots

Neighborhood in Abstraction Space

Data Class sits in a moderately populated region (54th percentile for distinctiveness): it has near-neighbors but no dense thicket of look-alikes.

Family — Property Ontology & Code Smells (18 abstractions)

Nearest neighbors

Computed from structural-signature embeddings · 2026-10-25