我正在学习域驱动设计,我对实体和将域服务注入其中有点困惑。我发现了这个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原则,我认为。
我找到了一些解决方案:
使用服务定位器。但它也有气味,因为它是一种反模式,如果我们使用它,很难测试和管理实体。
直接在密码方法中实施哈希alghoritm。
坚持我拥有的东西:)上面提到了缺点,专业是我的classess更容易测试,因为我可以提供模拟服务而不是完全实现。
我个人倾向于将我的代码重新反映到第二个解决方案,因为它没有破坏SRP(或者它确实?:)),classess不依赖于散列服务实现。还有什么? 或者您有其他解决方案吗?
答案 0 :(得分:5)
我对DDD很陌生,但我相信散列密码不是域的关注点,而是技术问题,就像持久性一样。哈希服务应该在域中定义它的接口,但它在基础结构层中实现。 应用服务然后使用哈希服务来哈希密码并创建Password
实例(应该是值对象 )在将其传递给User
聚合根之前。
可能存在聚合必须使用服务的情况,例如依赖项解析非常复杂且特定于域。在这种情况下,应用程序服务可以将域服务传递给聚合方法。然后聚合将双重调度到服务以解析引用。
有关更多信息,请阅读Vaughn Vernon撰写的“实施领域驱动设计”一书。他在第362页(模型导航)中谈到了这一点,但也在本书的其他几个地方。
答案 1 :(得分:0)
我不知道原因,为什么只考虑注入构造函数参数。 AFAIK,它是DI容器注入属性或字段的常见功能。例如,使用MEF你可以写下这样的东西:
class SomeUserService : ISomeUserService
{
[Import]
private IHashGeneratorService hashGeneratorService { get; set; }
// ...
}
并仅在那些您真正需要它的类型中注入依赖项。