I had a few questions around value objects in DDD.
I had a class ReportDefinition
(basically a schema for a specific report that users can create) with the following properties (in Java):
UUID id;
UUID ownerId;
UUID organizationId;
ReportDefinitionName name;
ReportTitle reportTitle;
ReportDescription reportDescription;
Instant createdAt;
Instant lastModifiedAt;
As you can see, there everything is a value object. So far, all the value objects in the systems have been wrappers around a single primitive (Strings for the most part). i.e. to get the report title as a string, I would do reportDefinition.reportTitle().title()
.
Yesterday I needed to add another property, ReferenceType
. I came to the realization that reportTitle
, reportDescription
and the referenceType
should probably be grouped together another value object, ReportDetails
. I can see the need for referenceType
to still be a value object, but not sure if I should change title
and description
to simple strings within the ReportDetails
?
At the moment, ReportTitle
and ReportDescription
do some simple validations (i.e. report title must be between 1 and 60 characters, etc), but that's about it. If I made them strings, I could still do the basic validation checks within ReportDetails
.
Another question that arises is whether this might lead to violation of the law of demeter. i.e. it's like that the application layer will need to call reportDefinition.reportDetails.title().title()
which is starting to get a bit crazy.
And finally, given that value objects should be immutable, I'm guessing if I wanted to update the report details, I might need methods like ReportDetails updateTitle(title)
that returns a new instance of ReportDefinition
. I was wondering who should actually call that method? i.e. should it be a protected method within ReportDetails
and called via a void updateTitle()
method on the ReportDefinition
? Or is it fine for the application layer to call reportDetails.updateTitle()
and then pass the result to an updateReportDetails()
method on ReportDefinition
?
Part of the reason why it made sense to split them out into a seperate object was because a Report
will need the information in ReportDetails
when being created (whereas the rest of the information in ReportDefinition
is irrelevant and deals with other concerns).
The immutability is an artefact of following DDD and not wanting consumers of the class to modify ReportDetails
simply on account of having a reference to it. A business case is that ReportDetails
can only be change when ReportDefinition
is in a draft state (not shown above) whereas the other information in ReportDefinition
can always be changed.
ReportDetails
is the first enhancement, immutability is the second. Have you considered whether the benefits you derive from these enhancements outweigh the additional complexity that they bring to your project?