1

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.

  • 2
    Your problems seem to have started when you decided to add some "enhancements" without justifying them. 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? – Robert Harvey May 31 '18 at 21:26
  • @RobertHarvey added more details to the question in response to your comment – NRaf May 31 '18 at 23:11
3

Immutable is a wonderful thing, but the main reason why is not because DDD said so, because of threads, or even because it protects against nonsense. No the main reason is because immutable value objects are easier to read. Easier to reason about.

But they come at a cost. You only get one moment in time to build them.

I really don't understand why you're going through 4 layers to get a title when report.getTitle() should be the end of it.

  • There's a distinction between a ReportDefinition and a Report. A ReportDefinition is a template for a specific type of Report, alongside some other information (i.e. the ReportDefinitionName is the name of the ReportDefinition and nothing to do with Reports).The Report itself will have a getter to get the title. Think of ReportDetails.ReportTitle as being a templated string. A specific instance of a Report has some template variables that are used to resolve the title. – NRaf Jun 1 '18 at 4:03
  • Still don't understand. reportDefinition.getTitle() gets the report definition's title. I can't see what reportDefinition.reportDetails.title().title() is getting you. What's wrong with reportDefinition.getTitle().toString()? – candied_orange Jun 1 '18 at 19:29
  • A ReportDefinition has a name (reportDefintion.getName()) but the title isn't a direct child of ReportDefinition, it's part of the schema / template for a Report that ReportDefinition has. That schema has a number of other fields as well that are all used to help build a Report. The ReportDefinition has other fields which don't relate to building Reports (i.e. isDraft, createdTime, ownerId, etc), which is why I've created the ReportDetails object. – NRaf Jun 1 '18 at 23:24
  • With that said, I can do what you suggested, however I feel that reportDefinition.getReportDetails().getTitle() might make the API a bit clearer. i.e. having a method getName() and getTitle() on the ReportDefinition might be confusing to a user (what's the difference between the two?). Don't mind the toString(), I think that's better than having .title() at the end of the chain. – NRaf Jun 1 '18 at 23:24
  • 1
    Then in those cases where it's passed in you don't have a Demeter problem. You can just use reportDetails.title(). But remember Demeter is not just a dot counting exercise. – candied_orange Jun 2 '18 at 9:55
1

It looks like you have quite a complex object there. Too complex for a specific answer. But I would make the following observations:

  • It seems like a bad idea to enforce business rules via immutability.
  • Having all you rules in property setters is also somewhat confusing.
  • Add some methods to the top level object ie. Report.ChangeTitle(string title) and hide the complexity of report.reportDetail.reportTitle.title behind them.

An object which its impossible for the programmer to break rules with, due to immutability, setters, private methods or clever tricks with builders and interfaces is a wonderful thing to see and use.

But its much harder to achieve than an object which is impossible for the user to break rules with.

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.