A team wants to keep a full history of every state an `Order` entity passed through (e.g., for auditing or event sourcing) by storing immutable snapshots of the Order's data at each transition. Does storing these snapshots turn Order into a Value Object, and how should the snapshot type itself be modeled?
answer
- snapshot/event = Value Object, entity stays Entity
- event sourcing: identity threads a sequence of immutable events
- snapshot carries entity id as data, not as its own identity
- trade-off: audit trail vs. storage growth + replay/projection complexity
basics
~20 sNo — Order is still an Entity because it still has one identity across its whole life. The snapshots are a different, separate thing: each snapshot is a Value Object (immutable, no identity of its own) that just describes 'what the Order looked like at this specific point in time,' tagged with the Order's id and a timestamp.
solid answer
~50 sTaking immutable snapshots of an Entity's state doesn't change the Entity's classification — Order remains an Entity because the business still needs to say 'this is the same order' across all those snapshots; what changed is that you've introduced a new, separate concept (an OrderSnapshot or OrderState) to represent 'the Order's data as of time T.' That snapshot type is itself a textbook Value Object: it's immutable once created, it's fully described by its fields (which naturally include a reference to the Order's id and a timestamp, but that reference is just data it carries, not identity of its own), and two snapshots with identical field values genuinely are interchangeable. This is the pattern behind event sourcing and audit logs: the mutable, identity-bearing Entity is reconstructed by folding a sequence of immutable Value-Object-like events/snapshots, but the events themselves never become Entities just because they reference an Entity's id.
go deeper
Not expected to know event sourcing; should at least recognize that keeping a history of an object's past states doesn't erase that object's own ongoing identity.
Should recognize a snapshot/history record as a separate Value-Object-shaped type distinct from the Entity it describes.
Should be able to explain the event-sourcing pattern at a high level (append-only events, folded current state) and correctly classify both the Entity and its events.
Should weigh event sourcing's full cost/benefit trade-off for a real system (storage growth, projection/CQRS complexity, event schema versioning) and decide when a simpler audit log is the better call instead.
## Two different levels of the model It's tempting to think that once you start persisting a series of immutable, timestamped records of an Entity's state, the Entity itself has somehow 'become' a Value Object — after all, each individual record looks immutable and value-like. This conflates two different levels of the model. The `Order` Entity's defining property — a single, continuous identity (an `orderId`) that the business tracks across its entire lifecycle regardless of how its attributes change — hasn't gone anywhere. What's actually happened is the introduction of a second, distinct concept: a record type representing 'what the Order's data looked like at a specific point in time.' Call it `OrderSnapshot`, `OrderState`, or (in event-sourcing terminology) a domain event. That type is a genuine Value Object in its own right: - once created, it never changes (a snapshot from a given date doesn't retroactively change when the order is later modified — a new snapshot is created instead); - it's fully described by its fields (order id, status, line items, total, timestamp); - and two snapshots with identical field values, even from unrelated orders that happened to be in the identical state at the identical instant, are genuinely interchangeable in every meaningful sense — nothing distinguishes 'this snapshot object' from 'that snapshot object' beyond their data. ## Carrying an id is not the same as having one The subtlety worth calling out explicitly: the snapshot carries the Order's id as one of its fields (you need it to know which order the snapshot describes), but that doesn't give the snapshot its own identity — it's just data being carried, the same way a `Money` Value Object might carry a currency code without the currency code granting Money any identity. The test remains the same: would the business ever say 'these two snapshots are different even though every field matches' or 'these two snapshots are the same snapshot even though a field differs'? For a snapshot, the answer is no on both counts — two snapshots that agree on every field, including timestamp and order id, really are the same fact recorded twice, and a snapshot that differs in even one field is a genuinely different fact, correctly treated as unequal. That's pure **structural equality**, the signature of a Value Object. ## Where event sourcing builds on the split This pattern — an Entity whose full history is reconstructed by folding over a sequence of immutable Value-Object-shaped events — is the foundation of **event sourcing**, an architecture popularized in the DDD community. Instead of storing 'the current state of order #4711' as a mutable row that gets updated in place, the system stores an append-only sequence of immutable events (`OrderPlaced`, `LineItemAdded`, `OrderShipped`, `OrderCancelled`), each a Value Object describing one fact that happened, and the current state of the Order Entity is computed on demand (or cached) by replaying those events in order. The Entity's identity (`orderId`) is the thread that ties the whole event sequence together and lets you ask 'give me everything that ever happened to order #4711'; the events themselves are, individually, exactly the kind of side-effect-free, immutable, structurally-equal Value Objects the rest of this topic describes. ## The trade-off, named honestly The trade-off of this approach is substantial and worth naming honestly. **On the benefit side:** - a complete, tamper-evident audit trail comes for free (you can always answer 'what did the order look like on date X, and why'), - temporal queries and debugging production incidents become far easier, - and it naturally supports features like undo or what-if analysis. **On the cost side:** - storage grows unboundedly unless you introduce periodic snapshotting-for-performance (a different, purely technical kind of snapshot used purely to avoid replaying thousands of events from the beginning every time — not to be confused with the audit-trail snapshot above, though the two are often combined), - query patterns that used to be a simple current-state SELECT now require either replay-on-read or maintaining a separately updated read-model/projection (adding architectural complexity — the CQRS pattern frequently paired with event sourcing), - and schema evolution of the event types becomes a long-term versioning problem, since old events must remain replayable forever even as the domain model evolves. ## A banking ledger, concretely A concrete real-world case: a banking ledger system models each `Transaction` as an immutable Value-Object-shaped event (amount, from-account, to-account, timestamp) appended to an append-only log, while the `Account` Entity's current balance is a derived, cached projection computed by folding all transactions for that account's id. The Account retains a single stable identity across its entire history (you can always ask to see account #7's full transaction history), while every individual Transaction record is immutable and interchangeable-if-equal the moment it's written — exactly the split this pattern requires, and exactly why introducing history-tracking never demotes the underlying Entity to a Value Object.
- If events are Value Objects, why do many event-sourcing frameworks give each event its own unique event id?That id is typically a technical/operational concern — deduplication in an at-least-once delivery pipeline, ordering/positioning in the log, or idempotency for consumers — not a domain-identity concern; the event is still fully described by its business fields for equality purposes, the same way a database row can have a technical surrogate key without that key making the row's domain type an Entity.
- Does snapshotting-for-performance (periodic full-state snapshots to avoid replaying the whole event log) create Entities out of the snapshot records?No — a performance snapshot is still just an immutable capture of 'the folded state as of event N,' used purely to speed up reconstruction; it doesn't need its own trackable identity distinct from its data, so it remains Value-Object-shaped even though its purpose differs from an audit-trail snapshot's purpose.
- Is event sourcing required to get the benefits described here, or can a simpler audit log achieve something similar?A much simpler 'audit log' table that just appends immutable rows describing changes (without making those rows the sole source of truth for current state, which stays in a normal mutable table) captures most of the traceability benefit with far less architectural complexity — full event sourcing is worth its cost mainly when you need to reconstruct state at arbitrary points in time or support replay-driven features, not just for auditing.
A person's medical chart: the patient (Entity) has one identity across their whole life, but each entry in the chart — a lab result recorded on a specific date — is a permanent, unchangeable snapshot; correcting a mistake means adding a new dated entry, never erasing the old one, and two identical lab results recorded with the same values and date really are just the same fact.
saying these in an interview costs you the question
- Claims the Entity 'becomes' a Value Object once it's snapshotted or event-sourced
- Can't distinguish the entity's persistent identity from the snapshot's own lack of identity
- Assumes event sourcing is required to get any audit-trail benefit
- Doesn't recognize that an event carrying the entity's id as a field doesn't grant the event its own identity
- Ignores or is unaware of the storage-growth and read-model complexity costs of event sourcing