域服务注入域实体

时间:2014-01-27 10:47:04

标签: c# dependency-injection domain-driven-design

我正在学习域驱动设计,我对实体和将域服务注入其中有点困惑。我发现了这个blog,结论是向实体注入服务是一个坏主意。我部分同意这一点,但在这种情况下该怎么做: 我有User实体,它是一个聚合根,其中包含Password值对象。它看起来像这样:

密码值对象:

public class Password  
{  
    public string Hash { get; private set; }

    public string Salt { get; private set; }

    private readonly IHashGeneratorService _hashGeneratorService;

    public Password(IHashGeneratorService hashGeneratorService)
    {
        _hashGeneratorService = hashGeneratorService;
    }

    public void GenerateHash(string inputString)
    {
        //Some logic

        Salt = hashGeneratorService.GenerateSalt();
        Hash = hashGeneratorService.GenerateHash(inputString);
    }
}

用户实体:

public class User
{
    public Password Password { get; private set; }

    public User(IHashGeneratorService hashGeneratorService)
    {
        this.Password = new Password(hashGeneratorService);
    }
}

在这种情况下,如果我通过工厂创建User实体,我需要向工厂构造函数或Create()方法提供IHashGeneratorService实现。之后,如果我的工厂被使用,例如。 SomeUserService我必须提供实现(例如通过ctor注入)。等等... 老实说它闻起来像我,因为我的很多classess依赖于哈希生成器服务实现,但只有Password类使用它。它也打破了密码类的SRP原则,我认为。

我找到了一些解决方案:

  1. 使用服务定位器。但它也有气味,因为它是一种反模式,如果我们使用它,很难测试和管理实体。

  2. 直接在密码方法中实施哈希alghoritm。

  3. 坚持我拥有的东西:)上面提到了缺点,专业是我的classess更容易测试,因为我可以提供模拟服务而不是完全实现。

  4. 我个人倾向于将我的代码重新反映到第二个解决方案,因为它没有破坏SRP(或者它确实?:)),classess不依赖于散列服务实现。还有什么? 或者您有其他解决方案吗?

2 个答案:

答案 0 :(得分:5)

我对DDD很陌生,但我相信散列密码不是域的关注点,而是技术问题,就像持久性一样。哈希服务应该在中定义它的接口,但它在基础结构层中实现。 应用服务然后使用哈希服务来哈希密码并创建Password实例(应该是值对象 )在将其传递给User聚合根之前。

可能存在聚合必须使用服务的情况,例如依赖项解析非常复杂且特定于域。在这种情况下,应用程序服务可以将域服务传递给聚合方法。然后聚合将双重调度到服务以解析引用。

有关更多信息,请阅读Vaughn Vernon撰写的“实施领域驱动设计”一书。他在第362页(模型导航)中谈到了这一点,但也在本书的其他几个地方。

答案 1 :(得分:0)

我不知道原因,为什么只考虑注入构造函数参数。 AFAIK,它是DI容器注入属性或字段的常见功能。例如,使用MEF你可以写下这样的东西:

class SomeUserService : ISomeUserService
{
    [Import]
    private IHashGeneratorService hashGeneratorService { get; set; }

    // ...
}

并仅在那些您真正需要它的类型中注入依赖项。