Last Updated: March 25, 2026 at 15:30
Entities vs Value Objects in Domain-Driven Design
Entities and value objects form the foundation of every Domain-Driven Design model, yet the distinction between them is often misunderstood. Entities are defined by a continuous identity that persists through change—an order remains the same order even as its status and contents evolve. Value objects, by contrast, are defined entirely by their attributes and have no identity of their own—two address objects with the same street and city are indistinguishable. Mastering this distinction allows developers to model business concepts with precision, ensuring that code reflects not just data, but the true nature of how the domain operates

Introduction
In the previous articles of this series, we introduced the tactical building blocks of Domain-Driven Design. Among those building blocks, two appear repeatedly in almost every domain model: entities and value objects. They form the foundation upon which aggregates are built and business logic is expressed.
Yet for many developers new to DDD, the distinction between entities and value objects can feel subtle at first. Both are domain objects. Both contain data. Both can have behavior. So what truly separates them?
The answer lies in two fundamental concepts: identity and lifecycle.
Understanding when to model a concept as an entity and when to model it as a value object is not merely an academic exercise. It directly affects how you write code, how you ensure consistency, and how clearly your software reflects the business it serves.
This article explores the distinction in depth. We will examine what identity means, how equality is determined, how lifecycle differs between the two, and—most importantly—how to make practical modeling decisions through concrete examples.
Part One: Understanding Identifier vs. Attributes
What Is an Identifier?
An identifier is the mechanism that allows an object to be recognized as itself over time, even as its attributes change. It is the thread of continuity that ties together all the different states an object passes through during its existence.
Consider a person. A person changes many things throughout life: hair color, weight, address, even name. Yet we still recognize that person as the same individual. This recognition is possible because the person has a persistent identifier—a social security number, a passport number, or simply the unique fact of being that specific person—that remains constant despite superficial changes.
In software, an entity is precisely that kind of object. It carries an identifier that never changes, anchoring it through time.
Entities Have an Identifier
For an entity, attributes are secondary. They describe the entity at a point in time, but they do not define what the entity is. The identifier defines the entity.
Take an order in an e-commerce system. An order has an order number—a unique identifier assigned at creation. Over its lifetime, the order may have its shipping address updated. Items may be added or removed before shipment. Its status may change from pending to shipped to delivered. Through all of these changes, it remains the same order because its identifier—the order number—never changes.
If you were to compare two order objects with identical attributes—same customer, same items, same address, same status—you would still not consider them the same order unless they shared the same identifier. They would be two distinct orders that happen to look identical.
This leads to an important practical rule: for entities, equality is determined by comparing identifiers, not by comparing attributes.
Value Objects Have No Identifier
Value objects are different. They have no identifier. They are defined entirely by their attributes.
Consider a monetary amount of $100 USD. There is no identifier that distinguishes one $100 amount from another. They are interchangeable. If you have two instances representing $100 USD, they are considered identical. You would never ask, "Which $100 USD is this?" because the concept has no identity to track.
Consider a point in two-dimensional space defined by coordinates (10, 20). The point has no identifier. It is simply those coordinates. If you change the coordinates to (15, 25), you have not modified the original point—you have created a different point. There is no persistent identity that follows the point from one location to another.
Consider a color defined by RGB values (255, 0, 0) for pure red. Every instance of pure red is identical to every other. If you change the green value to 255, you have a different color—yellow—not the same color with a modified attribute.
For value objects, equality is determined by comparing all attributes. Two value objects with the same attributes are considered equal. If any attribute differs, they are different objects.
A Practical Rule of Thumb
Ask yourself this question: if I change one attribute of this object, does it remain the same object or become a different object?
If the object remains the same—because it carries an identifier that persists—it is an entity. An order that changes from pending to shipped is still the same order because its order number remains constant.
If changing an attribute produces a different object—because there is no identifier to anchor it—it is a value object. A point that moves from (10, 20) to (15, 25) is not the same point; it is a new point.
This distinction—identifier vs. attributes—is the most fundamental difference between entities and value objects. It determines how equality is evaluated, whether the object is mutable or immutable, how the object is stored and retrieved, and whether the object has a lifecycle that the system must track.
Part Two: Equality
How Equality Is Determined
The way we determine whether two objects are equal follows directly from whether they are entities or value objects.
For entities, equality is determined by identity. Two entity objects are considered equal if they share the same identifier, regardless of whether their other attributes differ. An order with ID 12345 that has been shipped is the same order as the one with ID 12345 that was previously pending. The state changed, but the identity remained constant.
For value objects, equality is determined by attribute values. Two value objects are considered equal if all their attributes match, regardless of whether they are distinct instances in memory. A Money object with amount 100 and currency USD is equal to any other Money object with the same amount and currency. This means value objects deduplicate naturally in sets and maps—a direct consequence of attribute-based equality, and one of the reasons they are so useful in collection-heavy domain logic.
Why This Distinction Matters in Code
This difference in equality has practical implications for how you implement these objects.
When implementing an entity, you typically override equality methods to compare only the identifier. Two instances of the same entity retrieved at different times—perhaps with different attribute values—should still be considered equal because they represent the same conceptual object.
When implementing a value object, you override equality methods to compare all attributes. Every field matters because there is no identity to fall back on.
A Note on Reference Equality
In programming languages, there is also reference equality—whether two variables point to the same object in memory. For both entities and value objects, reference equality is rarely what you care about. What matters is conceptual equality: do these objects represent the same thing in the domain?
Entities answer this question by looking at identity. Value objects answer by looking at attributes.
Part Three: Lifecycle
How Entities and Value Objects Live Differently
Entities and value objects have fundamentally different lifecycles in a domain model.
Entities have a lifecycle. They are created at some point, undergo changes over time, and may eventually be archived or removed. Throughout this lifecycle, their identity persists. You can track an entity from creation to final state because the identity provides a thread of continuity.
Consider a bank account. It is opened with an initial balance. Over months and years, deposits and withdrawals occur. The account may be frozen, reactivated, or eventually closed. Through all of this, the account number remains constant. The identity allows the system to track the account's history and ensure that operations refer to the correct account.
Value objects do not have a lifecycle. They are created, used, and discarded. They do not change over time because they are immutable. When you need a different value, you create a new instance rather than modifying an existing one.
Replacing, Not Mutating
When a customer upgrades their subscription from a basic plan to a premium plan, you do not modify the existing SubscriptionPlan value object. You create a new SubscriptionPlan instance with the premium features and associate it with the customer entity. The old plan is simply discarded—or, if audit history matters, explicitly retained alongside the new one.
Similarly, when a product's price changes from $50 to $55, you do not modify the existing Money value object. You create a new Money instance with the updated amount and associate it with the product. The previous price may be kept for historical order records, but the value object itself remains unchanged.
This replacing-rather-than-mutating pattern is a deliberate modeling choice with real consequences. Because there is no transition on the value object itself, any audit trail must be built explicitly in the entity or elsewhere. A sequence of value objects does not carry history on its own; history lives at the entity level. The entity tracks which value objects were associated at which points in time, while the value objects themselves remain immutable snapshots of those states.
Immutability and Its Implications
Value objects are immutable by definition. This immutability is not merely a technical preference; it reflects the nature of value concepts in the real world. A date range of January 1 to January 31 does not change. A new date range is a different value.
Immutability brings several benefits. Value objects can be shared safely without risk of unintended side effects. They are inherently thread-safe. They are easy to test because their behaviour is deterministic. And because they never change, they are easy to reason about.
Entities, by contrast, are mutable. Their entire purpose is to change over time while maintaining identity. This mutability requires careful management—which is why aggregates play such an important role in ensuring that changes to entities remain consistent.
Value Objects Containing Entity References
One nuance worth noting: value objects are not restricted to containing only primitives or other value objects. A value object may hold a reference to an entity's identity—for example, a ProductSnapshot value object might contain a productId alongside a captured name and price. What it must not do is hold a mutable reference that it can modify. The value object remains defined by what it contains at the moment of creation.
Part Four: Practical Modeling Examples
With the theoretical distinctions clear, we can turn to practical modeling. The following examples illustrate how to decide whether a concept should be an entity or a value object.
Example One: A Customer
Consider a customer in an e-commerce system. A customer has a name, an email address, a shipping address, and a loyalty tier. Over time, the customer may change their email address, move to a new home, and achieve a higher loyalty tier.
The customer clearly has an identity. Even after changing name, email, and address, the system still recognises this as the same customer. Orders placed in the past remain associated with this customer. The customer has a lifecycle: they register, make purchases, possibly become inactive, and may eventually close their account.
Customer should be an entity. Its identity—a customer ID—remains constant while its attributes change over time.
Example Two: An Address
Now consider the address associated with that customer. An address consists of a street, city, state, postal code, and country. Two addresses with the same values are the same address. If a customer moves, the entity does not update its existing address object—it replaces it with a new one. The address has no lifecycle of its own.
Address should be a value object. It is immutable, defined by its values, and has no independent identity.
Example Three: A Money Amount
Consider a monetary amount used for pricing products and calculating totals. An amount of $50.00 is simply that—an amount. There is no identity attached. Two $50.00 amounts are indistinguishable. Operations like addition produce new money objects rather than modifying existing ones.
Money should be a value object. It has no identity, is defined entirely by its amount and currency, and is naturally immutable.
Example Four: A Flight Reservation
Now consider a more nuanced example. In an airline reservation system, a flight reservation consists of a passenger name, a flight number, a date, and a seat assignment.
At first glance, one might model this as a value object. After all, a reservation for passenger Smith on flight AA123 on May 15 in seat 12A seems defined by its attributes.
However, consider what happens over time. The passenger may change their seat assignment. The flight may be delayed or rebooked. The reservation may be checked in, boarded, or cancelled. Throughout these changes, it remains the same reservation. A cancelled reservation is not a different reservation; it is the same reservation with a different status. The system needs to track this specific reservation across those transitions.
Reservation should be an entity. It has an identity—a reservation confirmation number—that persists through changes, and a lifecycle that includes creation, modification, and eventual completion or cancellation.
Example Five: A Date Range
Consider a date range representing a rental period, a subscription term, or a reporting interval. A date range has a start date and an end date. If you change either date, you have a different range—not the same range with a modified attribute.
A date range has no identity. It does not change over time; if a rental period is extended, you create a new date range representing the updated period rather than modifying the existing one.
DateRange should be a value object. It is immutable and equality is determined entirely by its two date values.
Part Five: When the Distinction Blurs
The Same Concept Can Be Modeled Either Way
In practice, some concepts can be modeled as either an entity or a value object depending on the context. The same real-world concept may require different treatment in different bounded contexts.
Consider a product in an e-commerce system. In the catalog context, a product is an entity. It has an identity—a SKU or product ID—and attributes like description, price, and inventory level change over time. The catalog context needs to track the product across those updates.
In the order context, however, a product is better modeled as a value object. When an order is placed, the product details at that moment—name, price, description—are captured as a snapshot. The order does not need to follow changes to the product after it is placed. If the product description changes later, the order retains the description that was current at the time of purchase. The concept is the same; the modeling decision differs because the domain need differs.
This is perfectly acceptable. The modeling decision depends on what the specific bounded context needs to know and track.
When to Reconsider
If you find yourself adding a unique identifier field to a value object—perhaps so you can look it up or update it independently—that is a strong signal the concept should be an entity. The moment something needs to be found by identity and changed over time, it has acquired the characteristics of an entity in disguise.
The reverse is also worth checking. If you have an entity with no meaningful behavior, attributes that never change, and no lifecycle to speak of, it may actually be a value object carrying an unnecessary identifier. Stripping the identity and treating it as immutable will simplify the model and reduce the surface area for bugs.
The distinction is not about dogma. It is about aligning the model with the domain. Ask honestly: does this concept have continuous identity that persists through change? Does the system need to track its lifecycle? If yes, it is an entity. If it is simply a collection of values that can be replaced wholesale when those values change, it is a value object.
Why the Distinction Matters
Understanding whether a concept should be an entity or a value object is not merely a theoretical exercise. The decision has profound implications for how you write code, how you maintain consistency, and how well your software reflects the business it serves.
Implications for Code Design
When you correctly identify an entity, you know it needs an identifier. You know it will be mutable. You know it will have a lifecycle that the system must track. You know that equality is determined by identifier, not by attributes. You know that repositories will retrieve entities by their identifiers.
When you correctly identify a value object, you know it needs no identifier. You know it must be immutable. You know it has no lifecycle of its own. You know that equality is determined by all attributes. You know that it can be freely shared and copied without concern for identity confusion.
Getting this wrong leads to subtle bugs. If you model a value object as an entity, you may introduce unnecessary identifiers and mutability where none is needed. The system becomes more complex than it needs to be. If you model an entity as a value object, you lose the ability to track its lifecycle. Changes over time become impossible to trace, and you may inadvertently treat two distinct entities as interchangeable when they are not.
Implications for Consistency
Entities are mutable by nature. Because they change over time, they require careful management to ensure consistency. This is why entities often reside inside aggregates—the consistency boundaries we will explore in later articles. The aggregate root controls access to entities, ensuring that changes happen in a coordinated way and that invariants are preserved.
Value objects, being immutable, are inherently safe. They can be shared freely without risk of unintended side effects. Because they never change, they do not require the same consistency protections as entities. This makes them ideal for representing concepts that are truly defined by their values.
Implications for Communication
Perhaps most importantly, the distinction helps you communicate with domain experts. When a domain expert speaks about a "customer" or an "order," they are talking about something with identity—something that persists through change. When they speak about an "address" or a "price," they are talking about a description—something that can be replaced entirely when it changes.
By modeling these concepts correctly, your code aligns with how the business thinks. The language of the code reflects the language of the domain. This alignment reduces misunderstandings and makes the software easier to evolve as the business changes.
A Foundation for Further Learning
The distinction between entities and value objects is foundational to Domain-Driven Design. Once you understand it, other concepts become clearer. Aggregates are clusters of entities and value objects. Repositories store aggregates by the identifiers of their root entities. Domain services operate on entities and value objects. Factories create them. Every other tactical pattern builds on the foundation laid here.
In short, mastering the distinction between entities and value objects is not optional for effective DDD. It is the first step toward building domain models that are expressive, maintainable, and faithful to the business they represent.
Conclusion
The distinction between entities and value objects is one of the most fundamental decisions in Domain-Driven Design. It shapes how you model the domain, how you implement behavior, and how you ensure consistency.
Entities are defined by identity. They change over time while remaining recognisable. They have a lifecycle the system must track. Equality is determined by who they are, not what they contain.
Value objects are defined by their attributes. They are immutable and interchangeable. They have no lifecycle of their own. Equality is determined by what they contain, not who they are. When a different value is needed, a new object is created and the old one is discarded—with any required history managed explicitly at the entity level.
Making this distinction correctly requires understanding the domain. The modeling decision becomes clear when you ask the right questions honestly—and when it is clear, the code that follows becomes more expressive, more aligned with the business, and easier to maintain over time.
In the next article, we will build on this foundation by exploring aggregates—how entities and value objects cluster together to form consistency boundaries, and how aggregates become the primary building blocks for implementing domain logic.
About N Sharma
Lead Architect at StackAndSystemN Sharma is a technologist with over 28 years of experience in software engineering, system architecture, and technology consulting. He holds a Bachelor’s degree in Engineering, a DBF, and an MBA. His work focuses on research-driven technology education—explaining software architecture, system design, and development practices through structured tutorials designed to help engineers build reliable, scalable systems.
Disclaimer
This article is for educational purposes only. Assistance from AI-powered generative tools was taken to format and improve language flow. While we strive for accuracy, this content may contain errors or omissions and should be independently verified.
