如何避免贫血域模型,或者何时将方法从实体转移到服务中

我有一个共同的情况,我正在寻找一些更有经验的DDD和领域build模经验的人的指导。

假设我开始构build一个博客引擎,第一个要求是文章发布后,用户可以开始发布评论。 这开始很好,并导致以下devise:

public class Article { public int Id { get; set; } public void AddComment(Comment comment) { // Add Comment } } 

我的MVC控制器是这样devise的:

 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"); } } 

现在一切正常,符合要求。 下一次迭代,我们得到一个要求,每次发布评论时,博客作者都应该收到一封电子邮件通知他。

在这一点上,我有两个select,我可以想到的。 1)修改文章需要一个IEmailService(在ctor?)或从一个静态引用获取一个EmailService到我的DI容器

1a)看起来相当丑陋。 我相信它打破了我的实体意识到服务的一些域模型规则?

 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)也似乎难看,我现在需要一个静态访问configurationDI容器。

 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)创build一个IArticleService,并将AddComment()方法移到这个服务上,而不是在文章实体本身上。

我相信这个解决scheme更清洁,但添加评论现在不易发现,需要一个ArticleService来完成这项工作。 似乎AddComment应该属于Article类本身。

 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"); } } 

所以我基本上是从领域build模经验丰富的人那里寻求build议。 如果我错过了一个更明显的解决scheme,请让我知道:)

我一般都不喜欢这两种解决scheme,因为服务选项不易发现。 如果没有可用的ArticleService,我不能再添加Comment到一个Article的实例。 它也感觉不那么自然,因为AddComment看起来像是一个明显的文章types的方法。

无论如何,我期待着阅读input。 提前致谢。

我认为这个特殊的问题可以通过域名事件来解决。

你有没有考虑过文章控制器本质上是传递一个消息/发布一个事件? 然后,任何“文章发布事件监听器”将消耗该消息并作出相应的响应; 在您的具体情况下,电子邮件通知程序将监听这些事件并进行configuration。 这样,文章发布位不需要知道任何关于电子邮件通知位的信息。

通过这个优秀的问题,让我在MSDN上阅读从Udi 使用域模型模式 。

HTH帮助其他用户。

我一直在努力研究如何提出这个问题,但却多次让我感到困惑。 你的问题当然不是! 谢谢

如果不使用域事件,可以使用Double Dispatch模式,并将AddComment逻辑放入域服务中。

这是这样的:

 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"); } } 

如果你愿意的话,你可以在AddComment条上保留Add Comment逻辑,而是使用CommentAdded()方法将域服务变成类似ICommentAddedProcessor的东西,并且在文章上添加注释以及ICommentAddedProcessor。

我认为,只要领域专家使用“什么时候”这个词就必须考虑域名事件或事件总线,这是一个典型的例子。

我已经写了一个详细的答案 ,描述了什么时候使用事件总线,这可能是一个很好的解读这个话题