This is the way how we solve this (since more than 15 years, when even the term "domain driven design" was not invented):
- when mapping the domain model to a database implementation or a class model in a specific programming language, you have a simple, consistent rule like "for each domain object mapped to a relational table, the primary key is "TablenameID".
- this primary key is completely artificial, it has always the same type, and no business meaning - just a surrogate key
- the "graphical version" of your domain model (the one you use to talk to your domain experts) does not contain primary keys. You don't expose them directly to the experts (but you expose them to anyone who is actually implementing code for the system).
So whenever you need a primary key for technical purposes (like mapping relations to a database), you have one available, but as long as you don't want to "see it", change your level of abstraction to the "domain experts model". And you don't have to maintain "two models" (one with PKs and one without); instead, maintain only a model without PKs and use a code generator to create the DDL for your DB, which adds the PK automatically according to the mapping rules.
Note that this does not forbid to add any "business keys" like an additional "OrderNumber", if you need thembesides the surrogate
OrderID. Technically theythese business keys become alternate keys when mapping to your database. Just avoid using these for creating references to other tables, always prefer using the surrogate keys if possible, this will make things a hell lot easier.
To your comment: using a surrogate key for identifying records is no business-related operation, it is a purely technical operation. To make this clear, look at your example: as long as you don't define additional unique-contraints, it would be possible to have two VoIPProvider objects with the same combination of (name,url), but different VoIPProviderIDs.