In DDD, Entities all should have ids that define them. The problem comes when you have to decide who assigns that id.

Database-provided ids (centralised)

Assuming you want the best DB performance, you’ll likely allow the DB to assign the ids upon insertion to the corresponding table.

This approach means that, in your application(s), entities will not have an id until they are persisted to DB.

There are 2 approaches to dealing with this lack of ids:

Optional id

You can design your entities to have an optional id.

This means entities should be handled differently depending on whether it’s present or not, since they represent the data at different stages in time.

This translates to an if [entity.id](<http://entity.id>) check everywhere they are used.

Value objects

You can create Value Object versions of your entities, made only for the purpose of inserting data to the DB.

The problem with this approach is that you probably will have to duplicate code, or create entities with fake ids that you will later transform into the value object for insertion.

It also means that your repositories will have a save(item: EntityVO) and a separate update(item: Entity) (which you would’ve likely had anyways)

Application-provided ids (distributed)

In this case, you’ll use UUID/GUID, ULID or other similar approaches to generate the ids on applications prior to DB insertion.

Although better on the DX side, this solution will likely cause DB insertions to suffer due to the randomness of the ids required to avoid collisions. A potential solution is using timestamp-based ids so insertions all virtually happen at the end of the tables’ indices.

Another potential issue are collisions themselves. Although highly unlikely, there have already been instances of UUID collisions, so be aware of that (remote) possibility.

Something else to keep in mind is this introduces infrastructure-level concerns to your domain, as you’re now tied to a specific standard/convention/technology to generate said ids. An abstraction should be made, such as declaring a domain-specific type for ids (e.g. private id: EntityId) that encapsulates the id generation itself, and only provides methods to check for equality , order, etc.