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
URL, as well as references like
Now let's say I want to build a web service that will allow privileged users to manage
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
- 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,