ConceptGENERALSTAGE-SPECIFICILLUSTRATIVE

Does It Have Identity?

Two carts with the same items are two carts, because each belongs to someone and will become a different order. That is identity, and it is the first question a definition has to answer — because it decides whether the thing needs an id, whether equality means "same contents" or "same thing", and what happens when two of them look alike.

The moveWorked exampleNext questions

The situation, the reflex, and why it stalls

Every lesson starts where being stuck starts: someone has a problem, and the first move that comes to mind feels like progress.

The question

Two carts hold exactly the same items. Are they the same cart? And what does your answer decide about the code?

The situation

You wrote the sentence. Now a colleague asks a question that sounds like a riddle: "if Alice's cart and Bob's cart both contain one laptop, how many carts are there?" You say two, obviously, and then cannot say what in the code makes that true.

The reflex

Skip the question — it is philosophy — and give the cart an id field because every tutorial's cart has one. Identity handled.

Why it stalls

The id is there, and nothing uses it. In a single in-memory demo there is one cart and the variable is its name; the field is a decision made by copying, and when persistence arrives nobody knows whether the id should be the owner, a UUID, or the session.

What the reflex produces — and fails to produce
  • The id is there, and nothing uses it. In a single in-memory demo there is one cart and the variable is its name; the field is a decision made by copying, and when persistence arrives nobody knows whether the id should be the owner, a UUID, or the session.
  • The question was skipped for the cart and therefore never asked of the cart item — and cart items are the opposite case. Two entries "Laptop × 1" are not two things; they are one thing counted wrong. Without the identity question, the duplicate entry is not recognisably a bug.
  • Equality is left to the language. Two carts compare equal if the runtime says so — by reference in one language, by contents in another — and the test that checks "the cart is unchanged" passes or fails depending on which.
ProblemUnderstandRequirementsConstraintsUnknownsDecompositionSmallest StepModelExperimentObserveDebugLearnIterate

The move

Precisely enough to apply it to a problem you have never seen — not a slogan.

  • Ask, of the concept, one question: if two of these have identical contents, are they one thing or two? A thing whose sameness is its contents is a value — a money amount, a date, a product id. A thing that stays itself while its contents change is an entity — a cart, an order, a user. The record answers for the cart: "yes, weakly. Two carts with the same items are still two carts, because each belongs to someone and will become a different order."
  • Read the consequences off the answer. An entity needs a way to be found again once it leaves the variable that holds it — an id, once it leaves memory; in memory, "the variable is the identity". A value needs no id, is compared by contents, and can be freely copied.
  • Ask the same question of every part. The cart is an entity. A cart *item* is not: "Laptop × 2" is not a thing with a life of its own, it is a fact about the cart, keyed by the product. That answer is the rule "one logical entry per product" seen from the other side — two entries for one product is a value duplicated, not two entities.
  • Notice the word "weakly". A cart's identity matters less than an order's: nobody references a cart from elsewhere, and merging two carts at login is allowed. An order's identity is strong — invoices, refunds and shipments all point at it. The strength of the identity decides how much the id must be protected.

Two carts, one laptop each

The shortest proof that a cart is an entity is to change one of two identical carts and watch the other stay put. If they were one thing, both would change. The changed list is deliberately about which cart moved, not about quantities — that is what identity is.

Identity, made visible
before
Alice's cart = [ Laptop × 1 ]; Bob's cart = [ Laptop × 1 ]
Alice adds Mouse →
after
Alice's cart = [ Laptop × 1, Mouse × 1 ]; Bob's cart = [ Laptop × 1 ]
what changed Alice's cart gained an entry; Bob's did not — two carts with the same contents were two carts · The catalog, the products and Bob's intentions: unchanged — the cart points at products, it does not own them · What made "Alice's" findable: in V0 a variable, in V3 the owner column — the identity exists before the id does

Entity or value, for each part

The same question, asked of the cart and of everything inside it. The answers are different, and the difference is what makes the duplicate entry a bug and the two identical carts not one.

If two of these had identical contents, are they one thing or two?

Two — an entity

when The cart. Each belongs to someone and will become a different order; it stays itself while its contents change.

cost Needs a way to be found again — a variable, a key, an id — and equality has to be written, not inherited.

One — a value

when A cart item ("Laptop × 2"), a product id, a money amount. Sameness is contents; two identical ones are a duplicate.

cost Cannot be referenced from elsewhere; a change is a replacement, which is exactly right for a quantity.

Depends on the version

when The owner. In V0 there is no owner and one cart; in V1 the owner is what distinguishes carts.

cost A decision deferred is a decision to revisit, with a note saying which requirement triggers it.

The rule that identity produces

"A cart item is a value keyed by the product" is the identity answer for the item, and read as a rule it is the one the record discovered from the second-add example. The code that encodes it is a find before an append — the same three lines the rules module derives from the other direction (Rules Determine Implementation).

From identity to code

rule One logical entry per product — an item has no identity beyond the product it points at.

becomes validation Before adding, look for an existing entry with the same product id; if it exists, change its quantity instead of creating a second entry.

becomes code
existing = find(cart.items, productId)
if existing: existing.quantity += quantity
else: append(cart.items, { productId, quantity })

The implementation ladder

Concept, examples, pseudocode, code, tests, production — for the concept this lesson is about. Code is the fourth tab, not the first.

concept Shopping Cart beginner
Build it step by step →

Shopping Cart = A temporary collection of products the user intends to purchase, held between browsing and checkout.

Identity, ownership, lifetime
  • Does a cart have identity? Yes, weakly. Two carts with the same items are still two carts, because each belongs to someone and will become a different order. It needs an id once it leaves memory; in memory the variable is the identity.
  • Who owns it? A shopper — a logged-in user or an anonymous session. The owner is part of the state because "my cart" has to be findable again.
  • How long does it exist? From the first add until checkout or abandonment. Whether it survives a reload, a closed browser or a login is not a property of the concept; it is a persistence decision made later, and each answer changes where the cart lives.
  • Should it survive reload? Usually yes for a store, usually no for a demo. V1 in memory says no; V2 browser storage says yes on one device; V3 server storage says yes everywhere the user is logged in.
  • Should it survive login? Only if an anonymous cart and a logged-in cart are merged — a rule that does not exist in V1 and appears as a modification later.
State it must remember
  • itemscollection of CartItemkeepThe cart is its items; without them nothing else means anything.
  • items[].productIdidkeepThe reference to what is being bought. The catalog owns the product; the cart only points at it.
  • items[].quantityinteger > 0keepTwo laptops is one entry with quantity 2, not two entries — the rule "one entry per product" needs a quantity to hold.
  • owneruser id or session iddependsSo the cart can be found again by the person it belongs to.
  • items[].productNamestringderiveIt would be convenient to render the cart without a catalog lookup.
  • items[].pricemoneydependsThe total needs a price per item.
  • totalmoneydropEvery screen shows the total.
  • currencycodedependsPrices need a currency to be added.
  • createdAttimestampdropAbandoned carts might be expired or emailed about.
Operations
  • update Add item the updated cart
  • delete Remove item the updated cart
  • update Change quantity the updated cart
  • read View items the list of entries — product id and quantity — for rendering
  • domain Calculate total the sum of price × quantity over the entries
  • delete Clear cart the empty cart
Rules that must always hold
  • Every quantity is greater than zero.
  • One logical entry per product.
  • The total is never negative.
  • An unknown product cannot be added.
  • Quantity cannot exceed available stock — if inventory is enforced here.

How to do it

Most important first.

  • Write the answer beside the sentence: "identity: yes/no, because…". The because is what the code will encode (Define the Concept).
  • For an entity, say where the identity lives at each stage — the variable in V0, an id once persisted — rather than adding an id field now (Who Owns It, and How Long Does It Exist?).
  • For each part of the state, ask the same question. The parts that turn out to be values are the ones you compare by contents and may store as plain data.
  • Decide equality explicitly in the tests: "unchanged" means same contents for a cart item and same cart for the cart. A test that relies on the runtime's default equality has not decided.
  • Check the answer against a merge, a copy and a delete: what should happen to two carts with the same items when the owner logs in? The answer only makes sense if the carts are two things (What Can Happen to It?).

Worked on a concrete problem

The move has to produce something. This is what it produced.

  • Cart: entity, weakly. Alice adds a laptop; Bob adds a laptop; the carts have the same contents and are two carts, because at checkout they become two orders. In V0 there is one cart and its identity is the variable; in V3 it is a row with an id and an owner. The id is added when the cart leaves memory, not before (The Cart Disappears).
  • Cart item: value. [Laptop × 1, Laptop × 1] is not two items; it is one fact recorded twice. The record's predict-the-bug case is exactly this — addItem that skips the find and appends — and the identity question is the shortest way to see why it is a bug: an item has no identity of its own beyond the product it points at.
  • Order, for contrast: entity, strongly. Two orders with identical lines are two orders with two numbers, two payments and two shipments. The id is part of the concept from the first line, not a persistence afterthought — which is the difference between "weakly" and "strongly".

How you know it worked

What now exists that did not before, and what question you can now ask.

  • For the concept and each part of its state, there is a written answer — entity or value — with the reason.
  • You can say where the identity lives in the current version (a variable, a key in a map, a row id) and when that changes.
  • The duplicate-entry bug is recognisable as a bug before any test: it violates "a cart item is a value keyed by product".

The questions you can now ask

The field this whole domain exists for. After this lesson, these are the questions to put to an unfamiliar problem.

Next questions
  • ?If two of these had identical contents, would they be one thing or two — and why?
  • ?For an entity, where does its identity live right now, and when does it need an id?
  • ?Which parts of the state are values, compared by contents and safe to copy?
  • ?What does equality mean in the tests, and did I decide it or inherit it from the runtime?

What can go wrong

How the move itself fails
  • Everything becomes an entity with an id — the cart, each item, each quantity change — and the state fills with ids nothing looks up, each one a field to keep consistent.
  • Everything becomes a value, and the cart is compared by contents; Alice's and Bob's carts test equal, and a "de-duplicate carts" job merges them.
  • The question is answered once and then ignored when the version changes. In V4 an anonymous cart and a logged-in cart are merged; the merge is only well-defined because the two carts were two entities, and the merge rule is the identity answer being used.
What the move costs
  • The entity/value distinction is a vocabulary the learner has to acquire, and for a concept where the answer is obvious the question is a delay.
  • Deciding identity per part produces a design that is right and unusual — a language's default equality is what most code relies on, and colleagues may expect it.
  • "Weakly" and "strongly" are judgements, not measurements; two engineers can disagree about how protected a cart id must be, and the lesson cannot settle it for them.
Misreads
  • "Identity means the thing needs an id field." An entity needs a way to be found again; in memory that is the variable, in a map it is the key, in a database it is the id. The field arrives with the stage that needs it.
  • "Values are the simple things and entities are the complex ones." A money amount with currency and rounding rules is a value; a cart with one field is an entity. The distinction is about sameness, not size.
  • "Two carts with the same items are the same cart because the customer cannot tell the difference." The customer can: one of them is theirs. Identity is about who the thing belongs to and what it will become, not about what it looks like right now.

Where this applies

Problem-solving advice is stated as universal far more often than it is. These labels say what each method is specific to — and where CONTESTED appears, the note gives the strongest form of the opposing view.

  • GENERALEvery concept has to answer whether two of it with the same contents are one or two, and the answer decides ids, equality and merging for a cart, a message, a job or a rate-limit bucket alike.
  • STAGE-SPECIFICIn V0 the cart's identity is a variable and the question is nearly academic; from V1 (several carts) and V3 (rows) the same answer becomes a key, an id and an owner column, so the answer does not change with the version but where it is encoded does.
  • ILLUSTRATIVEAlice, Bob, their laptops and the duplicated entry are the concept record's invented example, used to make sameness visible; no real store is described.

Where the depth lives

This domain asks the question and hands the answer off by name.