Consider adding a way to obtain the `Identifier` in the `AggregateRoot` interface?
Consider adding a way to obtain the Identifier in the AggregateRoot interface?
Introduction
I am new to DDD, please correct me if I am wrong, thanks.
My topic is about considering adding the "IdentifierObtainable" feature of the AggregateRoot interface.
Background and motivation
As far as I know, aggregate roots must have identifiers, which means that the behavior of "obtaining identifiers" is also necessary. Since this is a fact, why not reflect it in the AggregateRoot interface?
In addition, considering that in actual situations, it is common to obtain identifier operations for all aggregate root objects in a unified way. I am not sure whether this obtain operation practice is DDD pure, but I often create MyAbstractAggregateRoot class containing the signature of Identifier getId(). If jMolecules is willing to provide the behavior of "obtaining identifier", it will bring convenience to developers without any compromise, without violating DDD at all. Right?
How to do it?
Regarding how to add this feature, I have some basic and simple ideas to share for reference or polishing. The following is my analysis. I think method 3 is better, but they all lack depth of thinking.
Method 1
Add getId()/getIdentifier() method to the AggregateRoot interface
The identifier field of the DDD aggregate root may not be called id. Sometimes I like to do this: private final PersonNumber number, the behavior of getting the identifier is getNumber(). Therefore, adding getId()/getIdentifier() method may bring noise. There is no method definition like getDYNAMIC_NAME in Java, so this method may not work (except when we don't mind adding noise).
Method 2
We assume that to use the second generic type of the AggregateRoot<T extends AggregateRoot<T, ID>, ID extends Identifier> interface as the return type of the "get identifier" behavior of the AggregateRootClass, and there must be one and only one field of this type in the AggregateRootClass.
With this convention premise, we can use some means, such as reflection, to get the identifier of the aggregate root object during runtime.
But the rationality of this convention seems to be questionable. Some things to consider are:
- Should the identifier type of a certain aggregate root class be used only for the aggregate root class?
- Can there be multiple fields of this type in the aggregate root class?
- If the identifier polymorphism is taken into account, it will be more complicated.
Method 3
It is agreed that the AggregateRootClass should always have and only have one field annotated with @Identifier, and the type of this field is compatible with the second generic type of the AggregateRoot<T extends AggregateRoot<T, ID>, ID extends Identifier> interface. The provider will obtain the identifier through the field (usually reflection) or property (only getter required bean property, no setter required) way. The user can also specify the obtaining way, like JPA's @Access annotation.
Then, the AggregateRoot interface has a default implementation of the getIdentifier() method to return the identifier of the aggregate root object (the default method looks up the provider through the SPI mechanism or directly implements the acquisition logic in the method body). The user can override this method in the AggregateRootClass if the default behavior is not satisfactory, or implement and register a custom provider.
In addition, the jMolecules IDE plugin can also kindly remind developers when the AggregateRootClass is missing the @Identifier annotation, just like the JPA Buddy plugin will report an error when an @Entity class does not have an @Id annotation if you use IntelliJ IDEA.