I do not use lazy loading. My root aggregate have entities (collection navigation properties). I want my aggregate to be self-contained, responsible for itself, and follow the Single Responsibility Principle (SRP), and adhere to high cohesion and low coupling.
The problem is that the code that retrieves the root aggregate needs to include certain child entities depending on which way it wants to interact with the aggregate.
Example:
public class Blog // My root aggregate
{
public ICollection<Author> Authors { get; set; }
public ICollection<Post> Posts { get; set; }
public AddAuthor(Author author)
{
_authors.Add(author);
}
public AddPost(Post post)
{
_posts.Add(post);
}
}
If I want to add a author, I have to do:
var blog = _context.Blogs.Include(x => x.Authors).Single(x => x.BlogId == 1);
blog.AddAuthor(/* ... */);
And if I want to add a post, I would have to do:
var blog = _context.Blogs.Include(x => x.Posts).Single(x => x.BlogId == 1);
blog.AddPost(/* ... */);
But I feel this breaks encapsulation, because now my Blog aggregate is not self-contained, its functionality depends on how the caller has retrieved the aggregate from the DbContext (or the repository). If the caller did not include the necessary dependent entities then the operation on the aggregate would fail (since the property would be null).
I would like to avoid lazy loading because it is less suitable for web applications and performs worse due to executing multiple queries. I feel that having a repository with methods such as GetBlogWithAuthors
and GetBlogWithPosts
would be ugly. Do I have to create a repository method such as GetBlog
which always include all child entities? (this would a big, slow query, that would timeout).
Are there any solutions to this problem?