I have a common scenario that I am looking for some guidance from people more experienced with DDD and Domain Modeling in general.
Say I start out building a blog engine, and the first requirement is that after an Article is posted, users can start posting Comments on it. This starts fine, and leads to the following design:
public class Article { public int Id { get; set; } public void AddComment(Comment comment) { // Add Comment } } My MVC Controller is designed like this:
public class ArticleController { private readonly IRepository _repository; public ArticleController(IRepository repository) { _repository = repository; } public void AddComment(int articleId, Comment comment) { var article = _repository.Get<Article>(articleId); article.AddComment(comment); _repository.Save(article); return RedirectToAction("Index"); } } Now everything works fine, and it meets the requirement. Next iteration we get a requirement that every time a Comment is posted, the blog author should get an email notifying him.
At this point, I have 2 choices that I can think of. 1) Modify Article to require an IEmailService (in the ctor?) or obtain an EmailService from a static reference to my DI container
1a) Seems pretty ugly. I believe it breaks some Domain model rules that my entities are aware of services?
public class Article { private readonly IEmailService _emailService; public Article(IEmailService emailService) { _emailService = emailService; } public void AddComment(Comment comment) { // Add Comment // Email admin _emailService.SendEmail(App.Config.AdminEmail, "New comment posted!"); } } 1b) Also seems ugly, I now require a configured DI container which is accessed statically.
public class Article { public void AddComment(Comment comment) { // Add Comment // Email admin var emailService = App.DIContainer.Resolve<IEmailService>(); emailService.SendEmail(App.Config.AdminEmail, "New comment posted!"); } } 2) Create an IArticleService and move the AddComment() method to this service instead of on the Article Entity itself.
This solution is cleaner I believe, but Adding a comment is now less discoverable and requires an ArticleService to perform the work. It seems like AddComment should belong on the Article class itself.
public class ArticleService { private readonly IEmailService _emailService; public ArticleService(IEmailService emailService) { _emailService = emailService; } public void AddComment(Article article, Comment comment) { // Add comment // Email admin _emailService.SendEmail(App.Config.AdminEmail, "New comment posted!"); } } public class ArticleController { private readonly IRepository _repository; private readonly IArticleService _articleService; public ArticleController(IRepository repository, IArticleService articleService) { _repository = repository; _articleService = articleService; } public void AddComment(int articleId, Comment comment) { var article = _repository.Get<Article>(articleId); _articleService.AddComment(article, comment); _repository.Save(article); return RedirectToAction("Index"); } } So I am basically look for advice from people more experienced in domain modeling. If I am missing a more obvious solution please let me know :)
I generally dislike both solutions to be honest, because the Service option is less discoverable. I can no longer add a Comment to an instance of an Article without having an ArticleService available. It also feels less natural, since AddComment seems like such an obvious method on the Article type.
Anyway I look forward to reading the input. Thanks in advance.