The problem I needed to solve
When I started Dalo’s core, I wanted a card to retain its logical identity across Hokm and Joker. Changing its image should not create a new card in a hand or change an existing selection.
Implementation in this snapshot
CardInstance holds CardData and creates a separate InstanceId in its constructor. Suit and Rank come from the data; the ID belongs to the live instance rather than its image.
The design decision and its cost
This separation creates an additional object, but makes the distinction between a shared definition and a card inside a match explicit. Two cards displaying the same image are not necessarily the same instance.
Current boundaries
A fresh constructor-generated ID is not a complete match restoration solution. This class alone does not serialize hands or provide network synchronization.
Identity while moving a card
Consider a simple situation: a player selects a card while the interface sorts their hand. If selection stores only an array index, that index may identify a different card afterward. InstanceId provides a way to refer to the instance itself. CardInstance creates it with Guid.NewGuid, allowing a consumer to associate selection with a card and resolve its display position separately. This is an architectural use case, rather than a claim that every interface component follows that pattern.
Another boundary separates Data from CardInstance. Two cards may refer to one CardData definition while retaining different IDs. Suit, Rank and IsJoker are read from that definition; the instance does not make independent copies of those properties. This avoids disagreement between duplicated attributes, but requires careful management of the referenced data. A readonly data field prevents replacing the reference; it does not, by itself, make every property of the referenced object immutable elsewhere.
The constructor also rejects null with ArgumentNullException. For Dalo, this is useful because a card without a definition should fail before rendering or winner evaluation. However, the constructor has no parameter for restoring a previous ID. Saving a match or receiving a card over a network would require a separate policy for reconstructed identity. Creating another card with the same suit and rank does not restore the previous instance or automatically validate old references.
What this stage taught me
For me, card identity began at this boundary. As selection and animation became more involved, I could still refer to the same instance even when its place in the interface changed.




