3

I am designing a blog in domain driven design. I don't know how I should define models: Post and Comment. My database: enter image description here

A user can see a post and related comments. So I think that Post model can be the aggregate root and the model Post should contain a list of related comments.

On the other hand, an admin can see a list of all comments (he can accept or reject it) with a post title related to this comment. So I think that Comment model can also be the aggregate root and should contain a related post.

public class Post : Entity, IAggregateRoot
{
    public Guid PostId { get; private set; }

    public string Title { get; private set; }

    public string Content { get; private set; }

    public List<Comment> Comments { get; private set; }


    private Post() { } // for Entity Framework

    public Post(Guid postId, string title, string content)
    {
        PostId = Guid.NewGuid();
        Title = title;
        Content = content;
        Comments = new List<Comment>();
    }

    public void AddComment(string author, string content)
    {
        Comments.Add(new Comment(PostId, this, author, content));
    }
}

public class Comment : Entity, IAggregateRoot
{
    public Guid CommentId { get; private set; }

    public Guid PostId { get; private set; }

    public Post Post { get; private set; }

    public string Author { get; private set; }

    public string Content { get; private set; }
    public CommentStatus CommentStatus { get; private set; }


    private Comment() { } // for Entity Framework

    public Comment(Guid postId, Post post, string author, string content)
    {
        CommentId = Guid.NewGuid();
        CommentStatus = CommentStatus.New;
        PostId = postId;
        Post = post;
        Author = author;
        Content = content;
    }
}

public enum CommentStatus
{
    New = 1,
    Accepted = 2,
    Rejected = 3
}

But in that case one aggregate root contains the second aggregate root, so I think I am wrong. I don't have any idea how I should design these two models. :(

1
  • 2
    I disagree with the downvote. While the question is born from a misunderstanding of DDD; the question is well-formed and well explained.
    – Flater
    Feb 7 at 11:05

1 Answer 1

5

So I think that Comment model can also be the aggregate root and should contain a related post.

You are conflating aggregate roots with DTOs, and these are two very different things.

An aggregate root is considered the owner, and therefore scope) of its sub-aggregates. If Comment is an aggregate root, and Post is a sub-aggregate, then that specific post is scoped only to that specific comment. This doesn't make sense.

Another way of detecting that making Comment the aggregate root is not the correct approach is by realizing that posts can exist without a comment. The first commenter will logically be looking at a post that has no comments yet. But if your comment is the aggregate root, then you cannot create a post without first creating a comment. This doesn't make sense.

You need to separate how you design your domain entities (= aggregates) and how your return your data. These can be very different for very different reasons.

Your question only mentions considerations related to how you return your data. This does not help answer how your domain entities should be structured, it only reveals what kinds of DTOs you'll be returning from your application.

A user can see a post and related comments.

This suggests a DTO similar to:

public class PostDto
{
    public IEnumerable<CommentDto> Comments { get; set; }
}

An admin can see a list of all comments (he can accept or reject it) with a post title related to this comment.

I interpret this description to mean that an admin can see a list of comments across multiple posts, i.e. not all belonging to the same post.

This suggests a DTO of the following form (presumably a list of these DTOs would be returned):

public class CommentDto
{
    public PostDto Post { get; set; }
}

Ignore the reused class names here. I am not implying that the two snippets should use the exact same DTO class definitions. I am merely using the class name to suggest what kind of data the class will contain. Generally speaking, unless dealing with a pure REST api, you'll want to keep separate models for separate endpoints.

How you structure your DTOs has no bearing on how your domain entities should be structured. No matter how you design your domain, it will always be possible to have two endpoints that can return the above DTOs.

But in that case one aggregate root contains the second aggregate root, so I think I am wrong.

You're somewhat on the right track here. One aggregate root cannot contain another root, by definition of what a "root" is. However, an aggregate root can definitely refer to another aggregate root.

Your "containing" comment is part of your domain entity vs DTO confusion. While aggregate roots (= domain entities) do not contain one another, DTOs (which are mapped from domain entities) can contain one another, as shown in the above DTO snippets.


The most common design here is one where the Post is the aggregate root and Comment is a sub-aggregate.

However, for a sufficiently complex Comment domain entity, an argument can be made that Comment is a separate aggregate root, one which holds a required reference to a Post.
This is where we would turn away from pure DDD for practical considerations. If your application contains a lot of comment-handling logic, rendering it impractical to always have to load the related post and all of its comments; then you can define your comments as their own aggregate root so they can be handled without having to load an inordinate amount of unnecessary data.

In pure DDD, a comment is a subaggregate of a post, as the comment can only exist in relation to the post. However, practical performance considerations can introduce a reasonable argument to not stick to pure DDD. Pure designs tend to be very inconsiderate of performance considerations, as they tend to rigorously define a structure no matter the cost, and in the real world this is often an unrealistic standard (but far be it from me to tell you you can't stick to pure DDD either).

5
  • wow man, you helped me SO MUCH!!! Thank you, thank you :) Now I know that in my basic application Post should be the aggregate root and Comment should be the entity (the sub aggregate). And in query handlers I should return different DTOs - thank you :) Feb 7 at 12:49
  • 1
    @AleksanderChelpski: "However, for a sufficiently complex Comment domain entity, an argument can be made that Comment is a separate aggregate" -- You could also argue for two separate Comment entities. One specialized for Posts, and another specialized in how comments are flagged and moderated. It all depends on behavior. You might have Comment associated with Posts, but CommentModerationFlag for when a comment is flagged by end users (which is out of scope for Posts). The same idea can be represented as multiple aggregate roots in DDD. They should have different behaviors and classes. Feb 7 at 16:58
  • 1
    And I should clarify that the Comment associated with Posts would not be an aggregate root, but CommentModerationFlag would be, as an example. Feb 7 at 16:59
  • Regarding the Admin use case, we're talking about presentation layer, not domain. The CommentDTO should rather have a string PostTitle { get; set; } property which get valued by flattening the Comment/Post relationship.
    – ArwynFr
    Feb 10 at 19:01
  • @ArwynFr The focus of the snippet was more to show which object goes in which, rather than the precise properties to use. Also, consider primitive obsession here. A more specific type may be warranted, but OP's question doesn't contain enough info to make a conclusive call there.
    – Flater
    Feb 21 at 8:47

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.