我的团队非常努力地坚持使用Domain Driven Design作为架构策略。但是,在大多数情况下,我们的域实体非常有限。我们希望在我们的域实体上添加更多业务/域行为。
例如,Active Record将数据访问权限放在实体上。我们不希望这样,因为我们乐于使用存储库模式进行数据访问。
此外,我们将我们的软件设计为SOLID(Bob叔叔放在一起的五个软件设计原则)。因此,在设计我们的实体时,我们注意单一责任,开放闭合,liskov,接口隔离和依赖性反转对我们很重要。
那么,我们应该包括哪些行为?我们应该远离什么类型?
答案 0 :(得分:23)
距离我问这个问题差不多一年了,从那以后我和我的团队已经学到了很多东西。以下是我今天回答这个问题的方法:
域名应代表(在代码中)业务是什么或做什么(在现实生活中)。然后,域实体是在现实生活中发现的工件或演员。这些艺术品和演员有什么样的行为?所有的。反过来,域实体应该对它们采取什么样的行为?全部。
例如,在现实生活中,经理可以雇用一名新员工。域名的表示应包括“经理”和“新员工”等实体。经理是演员,在这里。
//newEmployee comes from somewhere else... possibly the UI
//someManagerId comes from the logged in user
var manager = _repository.Get<Manager>(someManagerId);
manager.Hire(newEmployee);
因此,经理实体在此模拟/反映现实生活中的行为。另一种方法是跳过作为演员的经理实体,并将他推到角落,这样一个繁重的“域名服务”可以完成所有工作......就像这样:
//newEmployeeService comes from somewhere else... possibly injected using IOC
newEmployeeService.Create(newEmployee, someManagerId);
在贫血领域,您可以使用此类域名服务来创建或雇用员工。它有效,但它不具有表现力,而且行为不是可发现的。谁做了什么?为什么经理需要创建新员工?
我想当我最初问这个问题时,我想尝试在我的实体中开始包含更多行为,但我真的不知道如何不将注释服务注入我的实体(例如,使用构造函数注入)。从那以后,我们学到了一些新的技巧,我们团队的实体也非常富有表现力。简而言之,这就是我们正在做的事情:
答案 1 :(得分:3)
如果您必须询问应该在域实体上放置什么行为,那么您可能不需要DDD。我试图在这里提供帮助,因为我试图将DDD安装到一个不属于它的地方有很多痛苦。
DDD甚至domain model是之后可以遵循的模式,发现域复杂性太高,任何其他模式都无法工作。所以只是CRUD不适合DDD。根据我的理解,当您拥有包含复杂业务规则的有界上下文时,DDD适合您需要在转换聚合根状态之前运行。所以我不会在复杂的定义中包含验证。
您希望在实体中添加的行为与您尝试解决的业务问题密切相关。应该关注持久性(存储库等)的关注(实际上,持久性可能存在于工作流或事件存储中)。
希望这有帮助。
答案 2 :(得分:1)
我尝试将某些行为放入我的域,实体或值对象中。
持久性之前的验证。 在转换到新状态之前进行验证。例如,订单聚合根实体可以在进入Sent状态之前验证其内部状态及其聚合子代。 最小化get set属性并尽可能多地使用value对象。首先,它使模型更富有行为。实体变得更具描述性。第二,如果你必须在个人实体上使用值对象方法(如ApplyAdress方法),那么你很少会将你的实体置于无效状态,这个方法将Adress Value对象作为参数。
还有...... 信息情报。使用您的实体及其值对象来控制和限制聚合信息。像personidentity可以是一个处理一个人的独特性的价值对象。它包含ssn,ssn algoritm,处理ssn上的性别校验等。
答案 3 :(得分:0)
您实体上的行为应反映业务模型。可以对该实体做什么或由该实体做什么是商业世界应该是可以由实体类完成的东西。例如:
在在线购物系统中,您可以将产品添加到购物车。所以Cart类看起来应该是这样的:
public class Cart
{
//...
public void AddProduct(Product product)
{
...code to add product to cart.
}
}
可以说,方法应该反映用例。