A cache is built as `Map<Order, InvoiceDraft> pendingInvoices = new HashMap<>();`, keyed by the `Order` aggregate itself, with `equals`/`hashCode` overridden to include `status` because "two orders are only really the same if they're in the same state." Orders go into the cache while `OPEN`, and `pendingInvoices.get(order)` starts returning `null` for orders that are definitely still in the map. Walk through exactly what happens, bucket by bucket.
HashMap.put computes hashCode() once, when the order is inserted, and uses that value to pick a bucket; the entry then sits in that bucket permanently, with no way for the map to notice if the object's fields change afterward. Because hashCode() here includes status, an order inserted while OPEN lands in the bucket for hash(status=OPEN, ...). The moment the order's status changes to something else — markPaid(), ship(), whatever the aggregate's own lifecycle does — a later get(order) recomputes hashCode() fresh from the object's current fields, gets a different hash because status is now different, and looks in the bucket for that new hash instead — a bucket the original entry was never placed in. The entry isn't gone; it's sitting exactly where it was put, unreachable from any lookup that recomputes the now-different hash, which the domain-modelling lesson names as the specific mechanism this course warns against: never base hashCode() on a field that changes after the object might be stored in a hash-based collection.