Loading learning content...
Every software system models some aspect of the real world—or at least a simplified, useful abstraction of it. Whether you're building an e-commerce platform, a banking application, a social network, or a video game, you're representing things from your problem domain as objects in code.
These "things" are what we call entities. They are the nouns of your system, the concepts that have identity, meaning, and purpose. Understanding entities deeply is the first step toward designing systems that are both intuitive and maintainable.
By the end of this page, you will understand what entities are, how to identify them from problem descriptions, and why getting entities right is foundational to all subsequent design decisions. You'll develop the skill to see through problem complexity and extract the core concepts that will become your classes.
An entity is a distinct, identifiable concept from your problem domain that has meaning independent of its properties. In simpler terms, an entity is a "thing" that:
Let's be precise about what this means.
Ask yourself: "If I swap all the properties of object A with object B, have they become each other, or are they still distinct?" For entities like User or Order, swapping properties doesn't swap identity—Alice is still Alice even if she changes her name. For value objects like an Address or Money, identity IS the properties—$50 is $50 regardless of which specific instance you hold.
Identity is the defining characteristic of an entity.
Consider a User in a social media application:
user_12345)user_12345, the same account, the same entityThis persistence of identity through change is what makes something an entity. It's also why entity design is critical: entities form the stable backbone around which your system's behavior is organized.
| Characteristic | Description | Example |
|---|---|---|
| Unique Identity | Each instance can be uniquely identified, typically by an ID | User ID, Order Number, Transaction ID |
| Lifecycle | Entities are created, modified, and potentially destroyed over time | An order is placed, shipped, delivered, or cancelled |
| Mutable State | Properties can change while identity remains constant | User changes email, product changes price |
| Domain Significance | Entities represent core business concepts | Customer, Invoice, Appointment, Reservation |
| Trackability | History and changes can be tracked over time | Audit logs, versioning, change history |
The concept of entities is central to Domain-Driven Design (DDD), a software design philosophy that emphasizes building software models that closely mirror real-world domains. In DDD terminology, entities are one of the fundamental building blocks, alongside Value Objects, Aggregates, and Services.
While we'll explore these other concepts later, understanding the DDD perspective on entities provides valuable insight:
"An entity is an object that is defined not by its attributes, but by a thread of continuity and identity." — Eric Evans, Domain-Driven Design
This definition captures the essence: entities are about continuity and identity, not about their current state.
While DDD is often associated with complex enterprise systems, its entity concepts apply universally. Even simple CRUD applications benefit from thinking clearly about which concepts are entities versus value objects, and how identity should work across your domain.
One of the most critical skills in Low-Level Design is the ability to read a problem description and identify the core entities. This isn't just about finding nouns—it's about recognizing which nouns represent concepts central to your domain.
Let's explore a systematic approach to entity identification.
The Noun Extraction Method (and Its Limitations)
A classic technique is to underline all nouns in a problem statement. While this is a reasonable starting point, not all nouns become entities:
The skill lies in filtering this initial list down to true entities.
Worked Example: Online Bookstore
Consider a requirement: "Users can browse books, add them to a shopping cart, and place orders. Each order contains multiple items and is shipped to a delivery address."
Let's identify the nouns:
Now let's analyze each:
| Noun | Entity? | Reasoning |
|---|---|---|
| User | ✓ Yes | Has unique identity (user ID), tracked over time, has lifecycle (registration to deletion) |
| Book | ✓ Yes | Has unique identity (ISBN/book ID), cataloged independently, persists across orders |
| Shopping Cart | ✓ Maybe | Could be an entity (persisted cart) or a transient collection. Design decision based on requirements. |
| Order | ✓ Yes | Has unique identity (order number), clear lifecycle (pending→confirmed→shipped→delivered), tracked historically |
| Order Item | ✓ Entity/VO | Has identity within an order, but may not exist independently. Often modeled as a child entity of Order. |
| Delivery Address | ✗ Value Object | Typically no identity—defined entirely by its properties. May be copied, not referenced. |
Notice how Shopping Cart and Order Item have nuanced answers. This is normal. Whether something is an entity, a value object, or a mere attribute often depends on your specific requirements. The key is to make conscious decisions, not accidental ones.
As you gain experience, you'll notice recurring patterns in how entities are structured and related. Recognizing these patterns accelerates your design process and helps you avoid common pitfalls.
Let's examine several fundamental entity modeling patterns:
Pattern 1: Root Entities and Child Entities
Some entities serve as "roots" that own and manage other entities. The root provides a consistency boundary—operations on child entities go through the root.
Example: An Order (root) contains OrderItems (children). You don't create or modify OrderItems independently; you add/remove them through the Order. This pattern is formalized in DDD as an Aggregate.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354
// Order is the root entity (aggregate root in DDD terms)class Order { private readonly id: string; private items: OrderItem[] = []; private status: OrderStatus = OrderStatus.Draft; constructor(id: string, customerId: string) { this.id = id; this.customerId = customerId; } // Child entities are managed through the root addItem(product: Product, quantity: number): void { // Root enforces business rules if (this.status !== OrderStatus.Draft) { throw new Error("Cannot modify confirmed order"); } const existingItem = this.items.find(i => i.productId === product.id); if (existingItem) { existingItem.increaseQuantity(quantity); } else { this.items.push(new OrderItem(product.id, product.price, quantity)); } } confirm(): void { if (this.items.length === 0) { throw new Error("Cannot confirm empty order"); } this.status = OrderStatus.Confirmed; }} // OrderItem is a child entity within the Order aggregateclass OrderItem { private readonly productId: string; private readonly unitPrice: Money; private quantity: number; constructor(productId: string, unitPrice: Money, quantity: number) { this.productId = productId; this.unitPrice = unitPrice; this.quantity = quantity; } increaseQuantity(amount: number): void { this.quantity += amount; } get total(): Money { return this.unitPrice.multiply(this.quantity); }}Pattern 2: Reference Entities vs. Embedded Data
When entities relate to each other, you have a choice: reference by ID or embed the related data.
customerId, not the entire Customer object. Advantages: data stays consistent, entities remain independent.Pattern 3: State-Based Entities
Many entities have explicit states that govern their behavior. An Order might be Draft, Confirmed, Paid, Shipped, Delivered, or Cancelled. A User might be Pending, Active, Suspended, or Deleted.
State-based design means:
If your entity has states, document the valid transitions. An order can go from Confirmed to Shipped, but not from Delivered back to Draft. Violating state invariants is a common source of bugs in complex systems.
Entity design seems straightforward until you've made these mistakes a few times. Let's examine the most common pitfalls and how to avoid them.
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455
// ❌ ANTI-PATTERN: Anemic Entity (just data, no behavior)class AnemicOrder { public id: string; public items: OrderItem[] = []; public status: string; public customerId: string; public total: number; // All getters/setters, no business logic} // Someone else has to validate business rules!function addItemToOrder(order: AnemicOrder, product: Product, qty: number) { // Where do we check if order is modifiable? // Where are the business rules? // This logic is scattered across the codebase order.items.push({ productId: product.id, qty }); order.total = calculateTotal(order.items);} // ✅ CORRECT: Rich Entity (encapsulates behavior)class Order { private readonly id: string; private readonly items: OrderItem[] = []; private status: OrderStatus = OrderStatus.Draft; constructor(id: string, customerId: string) { this.id = id; this.customerId = customerId; } // Behavior lives WITH the data addItem(product: Product, quantity: number): void { if (this.status !== OrderStatus.Draft) { throw new InvalidOperationError("Cannot modify non-draft order"); } if (quantity <= 0) { throw new ValidationError("Quantity must be positive"); } // Encapsulated logic: find existing, update or create const existing = this.items.find(i => i.productId === product.id); if (existing) { existing.updateQuantity(existing.quantity + quantity); } else { this.items.push(new OrderItem(product.id, product.price, quantity)); } } // Calculated property, always consistent get total(): Money { return this.items.reduce( (sum, item) => sum.add(item.subtotal), Money.zero() ); }}If you can describe what your entity IS and what it DOES in a single sentence that a domain expert would understand, you're on the right track. 'An Order represents a customer's confirmed purchase, tracks its items, calculates totals, and manages its lifecycle from creation to delivery.' Clear and behavior-rich.
Based on the patterns and anti-patterns we've explored, here are guiding principles for designing effective entities:
EmailAddress type that validates format is better than a nullable string. An enum for status is better than a magic string.These are guiding principles, not absolute rules. Context matters. Sometimes pragmatic violation of a principle is the right call. But violate them consciously, understanding the tradeoffs, not accidentally out of ignorance.
Let's examine how entities manifest across different domains. This will help you develop intuition for entity identification in unfamiliar problem spaces.
| Domain | Key Entities | Key Design Considerations |
|---|---|---|
| E-Commerce | Customer, Product, Order, Cart, Payment, Shipment | Order lifecycle, inventory tracking, payment state machines, address handling (entity vs VO) |
| Banking | Account, Transaction, Customer, Card, Loan | Transaction immutability, account balance consistency, regulatory audit trails |
| Healthcare | Patient, Appointment, Prescription, Provider, Facility | Patient identity across systems, appointment scheduling, medication safety invariants |
| Social Media | User, Post, Comment, Friendship, Notification | User identity and privacy, content moderation states, relationship modeling |
| Ride-Sharing | Rider, Driver, Ride, Vehicle, Location | Real-time location, ride state machine, rating systems, surge pricing |
| Learning Platform | Student, Course, Lesson, Enrollment, Progress | Enrollment lifecycle, progress tracking, content versioning |
Notice the patterns:
We've covered the foundational concept of entities—the building blocks of your object-oriented designs. Let's consolidate the key takeaways:
What's Next:
Now that we understand entities as the core "things" in our system, we need to explore what makes them unique and describable. The next page examines attributes—the properties that describe entities, the distinction between essential and incidental attributes, and how to design attribute sets that are complete yet focused.
You now understand entities as the identity-bearing building blocks of your systems. You can identify entities from problem descriptions and avoid common design pitfalls. Next, we'll explore attributes—the properties that give entities their characteristics.