In almost all circumstances, primary keys are not a part of your business domain. Sure, you may have some important user-facing objects with unique indices (UserName
for users or OrderNumber
for orders) but in most cases, there is no business need to overtly identify domain objects by a single value or set of values, to anyone but perhaps an administrative user. Even in those exceptional cases, especially if you are using global unique identifiers (GUID), you will like or want to employ an alternate key rather than expose the primary key itself.
So, if my understanding of domain-driven design is accurate, primary keys need not and thus should not be exposed, and good riddance. They're ugly and cramp my style. But if we choose not to include primary keys in the domain model, there are consequences:
- Naively, data transfer objects (DTO) that derive exclusively from combinations of domain models will not have primary keys
- Incoming DTO's will not have a primary key
So, is it safe to say that if you are really going to stay pure and eliminate primary keys in your domain model, you should be prepared to be able to handle every request in terms of unique indices on that primary key?
Put in another way, which of the following solutions is the correct approach to dealing with identifying particular objects after removing PK in domain models?
- Being able to identify the objects you need to deal with by other attributes
- Getting the primary key back in the DTO; ie, eliminating the PK when mapping from persistence to domain, then recombining the PK when mapping from domain to DTO?
EDIT: Let's make this concrete.
Say my domain model is VoIPProvider
which includes fields like Name
, Description
, URL
, as well as references like ProviderType
, PhysicalAddress
, and Transactions
.
Now let's say I want to build a web service that will allow privileged users to manage VoIPProvider
s.
Perhaps a user-friendly ID is useless in this case; after all, VoIP providers are companies whose names tend to be distinct in the computer sense and even distinct enough in the human sense for business reasons. So it may be enough to say that a unique VoIPProvider
is completely determined by (Name, URL)
. So now let's say I need a method PUT api/providers/voip
so that privileged users can update VoIP
providers. They send up a VoIPProviderDTO
, which includes many but not all of the fields from the VoIPProvider
, including some flattening potentially. However, I can't read their minds, and they still need to tell me which provider we are talking about.
It seems I have 2 (maybe 3) options:
- Include a primary key or alternate key in my domain model and send it to the DTO, and vice versa
- Identify the provider we care about via the unique index, like
(Name, Url)
- Introduce some sort of intermediate object that can always map between persistence layer, domain, and DTO in a way that does not expose implementation details about the persistence layer - say by introducing an in-memory temporary identifier when going from domain to DTO and back,