0

In the Book "Implementing Domain-Driven Design" the author suggests to implement a repository method to provide the next application-generated (not database-generated) ID. Like so:

class MyRepo {
  public MyId nextId() {
    return new MyId(UUID.fromRandom());
  }
}

That would lead to code like this:

var id = repository.nextId();
var entity = new MyEntity(id, ...);

But although I can see the point that providing IDs is somehow the responsibility of a repository, I don't see an actual benefit of this implementation. Why not assigning the ID directly on object construction?

@Entity
public class MyEntity {
  @Id
  private MyId id = new MyId(UUID.fromRandom());
}

One could also argue that the identity is a central part of the entity itself.

But apart from this philoshophical difference, I see the benefit of creating the ID directly in the entity that no additional call to the repository is needed.

Do you see any advantage of providing the ID by the repository?

2 Answers 2

4

The advantages that I can see:

  1. Easier testing; injecting the ID as a dependency allows you to create the entity in a known state, which would help value-based assertions (Assert.assertSame());
  2. Injecting the ID allows the repository to recreate an entity from storage (i.e. repository.get(id)) without either preventing or overriding constructor logic in the entity;
  3. Injecting the ID allows for easy switching between generators. Perhaps you don't always want a v4 UUID (random), but a v5 instead (name-based);
  4. Identifier re-use: The ID might be used in logging, shared between processes over a text-based mechanism, or otherwise used in situations where the complete entity is not (or no longer) available.
1
  • 4
    5. Domain code is about business logic; Id generation is a technical detail. Generating the Id outside the domain keeps the domain clean.
    – Rik D
    Jul 27, 2021 at 6:35
1

One scenario that comes to mind, while trying to wrap my own head around the whole DDD approach, is if you're implementing event sourcing system and say you have events "aggregate TypeA created with id ", "aggregate TypeB created with id ". Then you have some other event that assigns TypeB's id to TypeA for referencing.

If you were to replay these events to re-construct your application state, having the ID generated in the constructor would cause some problems, as you wouldn't be able to control it.

In a project I've worked on I was generating IDs within the constructor, but by passing in a "PrimaryKeyer" function, which when called was creating a new UUID. When I was retriving serialised entities from storage, and I had a specific id I wanted to assign to my object instance, I was passing the "PrimaryKeyer" to the constructor as a simple function returning that particular id (quite deterministic and nothing random about it. I wanted my object to have that id).

Clearly I wasn't following the advice in the book, but the need to pass the id into the constructor came as a natural requirement for implementation and testing.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.