Avoiding anemic domain models or when to move methods from objects to services

I have a common scenario that I am looking for some recommendations from people more experienced with DDD and Domain Modeling in general.

Let's say I'm starting to build a blog mechanism, and the first requirement is that after publishing an article, 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 as follows:

 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. In the next iteration, we get the requirement that every time a Comment is sent, the blog author must receive an email notification.

At the moment, I have two options that I can think of. 1) Modify the article to require IEmailService (in ctor?) Or get EmailService from a static link to my DI container

1a) It seems pretty ugly. I believe this violates some domain model rules that my entities know about 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, now I require a configured DI container that 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 the IArticleService service and move the AddComment () method to this service, and not to the article object itself.

This solution is cleaner, I believe, but adding a comment is now less searchable and requires ArticleService to do the job. It seems that AddComment should belong to 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"); } } 

Therefore, I mainly seek advice from people more experienced in domain modeling. If I don't have a more obvious solution, let me know :)

I don’t like both solutions, to be honest, because the Service option is less clear. I can no longer add a comment to an article instance without an accessible article. It also seems less natural, as AddComment seems such an obvious method for the type of article.

In any case, I look forward to reading the input. Thanks in advance.

+45
c # inversion-of-control domain-driven-design bdd
Sep 28 '09 at 18:53
source share
5 answers

I think this particular problem can be solved elegantly with a domain event .

+23
Sep 28 '09 at 19:03
source share
β€” -

Do you think that the article controller essentially sends a message up / hangs the event? Then any "listeners published in the journal" will consume this message and respond accordingly; in your particular case, an email alert will listen for these events and configure for it. Thus, article publishing bits do not need to know anything about email notification bits.

+2
Sep 28 '09 at 19:02
source share

Looking through this beautiful question, I got to read using the domain model template from Udi to MSDN.

HTH helps other users.

I tried to figure out how to ask this question, but got confused several times. Your question, of course, is not! Thanks

+1
Feb 01 '10 at 7:59
source share

Without using domain events, you can use the Double Dispatch template and put the AddComment logic in the domain service.

Here's what it looks like:

 public class Article { public void AddComment(Comment comment, IAddCommentProcessor commentProcessor) { commentProcessor.AddComment(this, comment); } } public interface IAddCommentProcessor { void AddComment(Article article, Comment comment); } public class AddCommentAndEmailProcessor : IAddCommentProcessor { private readonly _emailService; public AddCommentAndEmailProcessor(EmailService emailService) { _emailService = emailService; } public void AddComment(Article article, Comment comment) { // Add Comment // Email _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); article.AddComment(comment, new AddCommentAndEmailProcessor(ServiceLocator.GetEmailService())); // Or you can use DI to get the Email Service, or any other means you'd prefer _repository.Save(article); return RedirectToAction("Index"); } } 

If you prefer, you can leave the logic for adding comments in the AddComment of the article and instead make the Domain Service something like ICommentAddedProcessor using the CommentAdded () method and add AddComment for the article and comment ICommentAddedProcessor.

0
Mar. 31 '14 at 19:27
source share

I think that whenever a domain expert uses the word β€œwhen”, you need to consider domain events or the event bus, this was a classic example in this sense.

I wrote a detailed answer that describes when to use event buses, perhaps this is well read in this section

0
Oct 28 '14 at 5:38
source share



All Articles