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

Mat*_*ger 45 c# bdd domain-driven-design inversion-of-control

我有一个常见的场景,我正在寻找一些有DDD和域建模经验的人的指导.

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

public class Article
{
    public int Id { get; set; }

    public void AddComment(Comment comment)
    {
        // Add Comment
    }
}
Run Code Online (Sandbox Code Playgroud)

我的MVC控制器设计如下:

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");
    }
}
Run Code Online (Sandbox Code Playgroud)

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

在这一点上,我有两个我能想到的选择.1)修改文章以要求IEmailService(在ctor?中)或从静态引用到我的DI容器获取EmailService

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!");
    }
}
Run Code Online (Sandbox Code Playgroud)

1b)同样看起来很难看,我现在需要一个静态访问的配置DI容器.

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!");
    }
}
Run Code Online (Sandbox Code Playgroud)

2)创建一个IArticleService并将AddComment()方法移动到此服务而不是文章实体本身.

我相信这个解决方案更清晰,但添加注释现在不易被发现,需要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");
    }
}
Run Code Online (Sandbox Code Playgroud)

所以我基本上都是寻求在域建模方面经验丰富的人的建议.如果我错过了一个更明显的解决方案,请告诉我:)

我一般不喜欢这两种解决方案,因为服务选项不易被发现.在没有ArticleService可用的情况下,我再也无法为文章的实例添加注释.它也感觉不太自然,因为AddComment在Article类型上看起来就像是一个显而易见的方法.

无论如何,我期待着阅读输入.提前致谢.

Mar*_*ann 24

我认为这个特殊问题可以通过域事件优雅地解决.

  • 这是一个基本的场景,而Domain事件甚至不在Evans的书中.那么人们是如何应对的呢? (3认同)