Domain Driven Design – Updating Part of Aggregate in Node.js

domain-driven-designnode.jsrepository-pattern

I'm playing around with DDD in a node.js project and struggling with updates to child entities from the aggregate root.

For the sake of example, let's say I have two domain objects where Event is my aggregate root:

class Event {
  _id
  _date
  _comments = []
}

class Comment {
  _id
  _name
  _content
}

I understand that DDD dictates that an EventRepository is in charge of saving any changes to the AR and child entities and hides all the persistence details. So I should call something like EventRepository.save(event) at this point and be done.

Let's say that an event currently has 3 comments and a user has edited the content of the second comment. The implication now is that my repository needs to fetch my event and comments and compare with my domain objects to figure out which need to be persisted. That's doable but seems inefficient when the reality is I know that only one comment needs to be updated. It would also be simple to add another repository for comments but now I've veered away from pure DDD. I've seen discussions of the Unit of Work pattern but I'm not sure how this ties into my repository. It would also be simple enough to just have some sort of _isDirty on all my domain objects but now I've polluted them with persistence information.

I'm not sure it's relevant to the design pattern I'm struggling with but I'm doing this POC with objection.js and Postgres. Appreciate any feedback.

Best Answer

Your fears are perfectly valid, and they are an everyday part of DDD.

You can design your domain in many ways. For one thing, always keep in mind that the aggregate is the boundary of transactional consistency. That means that a single aggregate must be saved atomically, while several aggregates may be saved in distinct transactions. Updates lost on one aggregate should not affect the other aggregates. I am sure you're well aware of these considerations, but very often people loose them from sight when modeling a demanding domain.

The second issue you are mentioning, the cost of fetching the entire aggregate, is also perfectly valid and it happens all the time. However, the question comes: Did you really prove by measuring that performance is poor? If there is evidence backing your fears, then you should take some action. Otherwise, you might consider doing nothing, too.

So, finally, to some practical advice. I don't know your domain and this might not be applicable, but did you consider promoting comment to be the aggregate, too? If one element of the aggregate is adding disproportionate cost compared to everything else, then it often makes sense to design around it and turn it into its own boss.

The second observation is purely practical. Since we are all programming for the real world, and performance is a realistic metric, I don't think that stepping aside from "pure DDD" and updating only one entity from an aggregate without truly materializing the entire aggregate is a crime. As long as that aggregate's consistency rules are observed, you won't make a mistake. This has a downside that some operations are treated special compared to other operations on the same aggregate, possibly leading to certain duplication of logic, but again - that is not a crime.

Note that on purely topological grounds, these two pieces of advice are isomorphic. Either you promote the comment to become an aggregate and then update one of them at a time, or you simply turn the blind eye and update one entity from an aggregate at a time... From point of view of the database, it will update one object inside a transaction. Therefore, these two approaches have the same end.