【发布时间】:2014-01-27 10:47:04
【问题描述】:
我正在学习领域驱动设计,但我对实体和向其中注入领域服务感到有些困惑。我发现了这个blog,结论是向实体注入服务是个坏主意。我部分同意这一点,但在这种情况下该怎么做: 我有用户实体,它是一个聚合根,其中有密码值对象。它看起来像这样:
密码值对象:
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);
}
}
在这种情况下,如果我通过工厂创建用户实体,我需要向工厂构造函数或 Create() 方法提供 IHashGeneratorService 实现。之后,如果我的工厂被使用,例如。 SomeUserService 我必须为其提供实现(例如,通过 ctor 注入)。等等...
老实说,我闻起来很香,因为我的很多类都依赖于哈希生成器服务实现,但只有 Password 类使用它。而且它也违反了我假设的 Password 类的 SRP 原则。
我找到了一些解决方案:
使用服务定位器。但它也有异味,因为它是一种反模式,如果我们使用它,就很难测试和管理实体。
直接在密码方法中实现散列算法。
坚持我所拥有的 :) 上面提到了缺点,优点是我的类更容易测试,因为我可以提供模拟服务而不是完整实现。
我个人倾向于将我的代码重构为第二种解决方案,因为它不会破坏 SRP(或者它会破坏?:)),类不依赖于哈希服务实现。还有什么? 或者您有其他解决方案吗?
【问题讨论】:
-
+1 用于基础设施层和应用程序层的使用以及值对象(这是我的错误,我在我的帖子中纠正了它:))但我不确定应用程序层是否应该负责用于创建用户实体。我认为最好将其留在工厂中,因为工厂创建的用户实体已初始化所有需要的字段,包括密码值对象。
-
我毕竟发布了一个答案 =P 好吧,如果实例化
User对象非常复杂,可以在工厂中完成(作为域服务实现?)。可以将相关服务注入工厂,以便在进入User聚合之前实例化Password值对象,就像在应用程序服务中所做的那样。 -
小心,你的
Password实际上不是一个值对象,因为GenerateHash改变了它的状态。 -
@Alexander Langer - 我认为值对象没有自己的身份,不能单独存在,但它们的状态可以改变
-
@MadMax 值对象应该是不可变的。如果值更改,则使用更改的值创建一个新的值对象,并用新值替换原始值对象。检查martinfowler.com/bliki/ValueObject.html 以供参考。
标签: c# dependency-injection domain-driven-design