3

Let's take an example: we have two related entities, like Question and Choice. This is a poll: it contains of single question that may have 2 or more choices. Each choice can be voted for by users, each vote is stored.

Obviously, on paper, Question would contain a collection of Choices. And probably we would have something like this:

public class Question {
    List<Choice> choices;
    public void assignChoice(Choice choice) {}
    public List<Choice> getChoices();
}

Question is an Aggregate that contains list of other entity, Choice.

Now, if the Choice is a value object, I would be fine. Repository should store the aggregate starting from root, so it would store a root entity and related value objects.

However, here we have aggregate of entities! In that sense I wonder: why do we need to maintain collection in Question at all? I mean, for view, we will have query read-only model. For commands we might not needed it at all.

So we can move from this:

Question question = new Question("Favorite color?");
question.assignAnswer(new Choice("red"));
question.assignAnswer(new Choice("blue"));

questionRepository.store(question);

to:

Question question = new Question("Favorite color?");
Choice c1 = new Choice(question, "red"); // or just: questionId instead of question?
Choice c2 = new Choice(question, "blue");// the same

questionRepository.store(question);
choiceRepository.store(c1, c2);

Now we do not need to 'assign' anything to the Question and we don't need to maintain the collection. For example, this collection may vary - it may be collection of just approved choices, or voted choices, or top 3 voted choices etc. We are not able to expressed what kind of relationship is that just by having getChoices().

So should we even go further to have a rule like:

Don't compose Aggregates with other Entities. Just have a root Entity and value objects. Use reverse relationship to express the connection between aggregate roots.

Make sense?

1
  • Should you be able to modify or delete a choice without any impact on some questions? Can you random access choices without knowing whether they belong to a question and which question is? Aug 12, 2020 at 11:33

2 Answers 2

1

In my opinion, one reason to let the Question be the aggregate root of the choices (and maintain a list of choices within the question) might be:

A choice does not exist on its own and is always related to a specific question (even as an entity). That's why I think it would make sense to access a choice only through its parent question.

Another advantage could be that whenever you change the question, you can easily check all related choices for invariants. Or when deleting the question, you can simply remove all child choices within the Delete operation of the question itself.

I agree that all of this could also be achieved via a reverse relation, but would be more complicated in my opinion. For example, if I would have a reverse relation and want to delete a question, I would have to iterate through all choices to pick the ones that are related to this specific question.

Concerning your point with the varying collections of choices inside the question: This is something were you could add some additional filter logic and in that way implement some domain logic within your question, like:

public class Question {
List<Choice> choices;
public List<Choice> getHighestVotedChoices();// will query choices and return highest voted choices
public List<Choice> getApprovedVotedChoices(); // will filter choices list "approved property"

}

4
  • All what you are saying make sense, but I don't see that in practice. For example 1) would you delete question and choices one by one? Me not, I would use repo for that with 1-2 queries. 2) By passing the Question (or questioneId) to Choices constructor we already express that choice can not exist without a question. 3) For voting we need just a Choice, so here we don't need to access it via question. And so on. Yeah, I am trying to question here the common logic.
    – lawpert
    Oct 31, 2014 at 23:44
  • 1
    @lawpert "I would use repo for that with 1-2 queries" which repo? Question repo or Choice repo? In either case, you are modifying the other one from whathever you choose. "For voting we need just a Choice, so here we don't need to access it via question." How are you going to ensure only one vote exists for given question and voter?
    – Euphoric
    Nov 1, 2014 at 12:29
  • @Euphoric QuestionRepo for deleting question with all choices and votes - in this command, Question is an aggregate and root not only of choices but votes, too. Agree on the second, we would need Question too (and User), but it is ok to reference it from a Choice. So my goal here is to minimize the relationships in the code. Method Poll.getChoices() is actually a query method, and that behavior (quering) maybe should be outside of the class. Moreover, having getHighestVotedChoices() in Question would require having repository referenced from the model object.
    – lawpert
    Nov 1, 2014 at 13:32
  • I start to think about the composites (don't know the better word). Since the objects relations depends on the exact use case. For example, we might need Poll with Questions to show it on UI. On the other case, we need a Choice (as a root) and VotesCount, Poll and User as other composites, when we do vote. So instead to hardcode the relationship with the Poll.getChoice why don't we use a class (so, not a method) that will describe/express that?
    – lawpert
    Nov 1, 2014 at 13:35
1

Choice has Reference to Question

Makes sense if this is a composite, vice an aggregate.

Inadequate Requirements Analysis

Don't compose Aggregates with other Entities. Just have a root Entity and value objects. Use reverse relationship to express the connection between aggregate roots.

Make sense?

No, it does not.

It's not possible to make such global, absolute, diktats. Your requirements, use cases, will inform the class hierarchies and relationships.

it may be collection of just approved choices, or voted choices, or top 3 voted choices etc. We are not able to expressed what kind of relationship is that just by having getChoices()

Sounds like the Choice class needs Approved and Votes properties. Now, getting the "top 3 voted choices" would be quite easy.

Consider: class Choice : IComparable<Choice> then you can Sort() a List<Choice> based on Votes

I agree that all of this could also be achieved via a reverse relation, but would be more complicated in my opinion. For example, if I would have a reverse relation and want to delete a question, I would have to iterate through all choices to pick the ones that are related to this specific question.

So what if a collection must be iterated. That's what they're for. The .net framework goes to a lot of trouble to help you do this in a whole lot of ways.

Besides, you do not have to "iterate throught all choices", because Question has a choices property. So, select a choice to delete, get its Question reference, get that question's choices reference. Now you have all the choices.

Class design is not database design

Choice c1 = new Choice(question, "red"); // or just: questionId instead of question?

This, et.al. sounds like you want your class design to reflect the database design. Consider this stackoverflow question

6
  • One question: would your Choice have getVotes()?
    – lawpert
    Nov 2, 2014 at 8:23
  • 1
    I believe that @lawpert here want's to say (based on the comments on another answer) is that model relationships is not identical to class hierarchy. In programming, when you have a collection of related objects - that is very strong statement. But in domain one relationship depends on particular use case. Like an User class may be different models - in the Polls world, there are PollsAuthor and PollsUser (and maybe AuthenticatedPollsUser and so on). The same thing with the relationships. This is how I understood it ;)
    – igor
    Nov 2, 2014 at 8:42
  • Using Sort() on collection as you described is not ubiquitous language, right? We need: top3VotedChoices(). Sorting here is just part of the implementation
    – lawpert
    Nov 2, 2014 at 10:03
  • Specific syntax varies for a given language. But sure, "sorting" is ubiquitous. And even in C# you could sort using LINQ or passing a sort function - these 2 variations can give you run-time flexibility for what/how to sort.
    – radarbob
    Nov 3, 2014 at 13:57
  • Yes, Choice.getVotes(). Clearly a "choice" has votes, per the problem description. Aside: to get "top3Choices()" this function belongs in the Question class because a question contains the choices.
    – radarbob
    Nov 3, 2014 at 14:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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