我的团队非常努力地坚持将领域驱动设计作为一种架构策略。但是,在大多数情况下,我们的域实体都非常贫乏。我们希望将更多的业务/域行为放在我们的域实体上。
例如,Active Record 对实体进行数据访问。我们不希望这样,因为我们很乐意使用存储库模式进行数据访问。
此外,我们将软件设计为 SOLID(鲍勃叔叔提出的五个软件设计原则)。因此,我们在设计实体时关注单一职责、开闭、liskov、接口隔离和依赖倒置对我们来说很重要。
那么,我们应该包括哪些行为呢?我们应该远离哪些种类?
我的团队非常努力地坚持将领域驱动设计作为一种架构策略。但是,在大多数情况下,我们的域实体都非常贫乏。我们希望将更多的业务/域行为放在我们的域实体上。
例如,Active Record 对实体进行数据访问。我们不希望这样,因为我们很乐意使用存储库模式进行数据访问。
此外,我们将软件设计为 SOLID(鲍勃叔叔提出的五个软件设计原则)。因此,我们在设计实体时关注单一职责、开闭、liskov、接口隔离和依赖倒置对我们来说很重要。
那么,我们应该包括哪些行为呢?我们应该远离哪些种类?
我问这个问题已经快一年了,从那时起我和我的团队学到了很多东西。今天我将如何回答这个问题:
域应该代表(在代码中)业务是什么或做什么(在现实生活中)。因此,域实体是在现实生活中发现的工件或参与者。那些现实生活中的人工制品和演员有什么样的行为?所有的。反过来,域实体应该对它们有什么样的行为?所有的。
例如,在现实生活中,经理可以雇用一名新员工。域的表示应该包括像“经理”和“新员工”这样的实体。经理就是演员,在这里。
//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);
在贫血的域中,您将使用这样的域服务来创建或雇用员工。它有效,但它没有表现力,并且行为不那么容易被发现。谁做了什么?为什么经理需要创建新员工?
我想当我最初问这个问题时,我想尝试开始在我的实体中包含更多行为,但我真的不知道如果不将服务注入我的实体(例如,使用构造函数注入)。从那时起,我们学到了一些新技巧,并且我们团队的实体具有超强的表现力。简而言之,这是我们正在做的事情:
如果您必须询问应该对域实体采取什么行为,那么您可能不需要 DDD。我想在这里提供帮助,因为我在尝试将 DDD 安装到它不属于的地方时遇到了很多痛苦。
DDD 甚至域模型是在发现域复杂度太高而无法使用任何其他模式之后可以遵循的模式。所以只是 CRUD 不适合 DDD。据我了解,当您有一个包含复杂业务规则的有界上下文时,DDD 适合这些规则,这些规则需要在转换聚合根的状态之前运行。所以我不会在复杂的定义中包含验证。
您希望在实体中放置的行为类型与您尝试解决的业务问题密切相关。应该关注持久性(存储库等)(事实上,持久性可能在工作流或事件存储中)。
希望这可以帮助。
我尝试将一些行为放入我的域、实体或值对象中。
持久化之前的验证。在转换到新状态之前进行验证。例如,订单聚合根实体可以在进入 Sent 状态之前验证其内部状态及其聚合子实体。尽可能减少 get set 属性并使用值对象。首先,它使模型的行为更加丰富。实体变得更具描述性。其次,如果您必须在将 Adress 值对象作为参数的人员实体上使用值对象方法(如 ApplyAdress 方法),那么您很少将实体置于无效状态。
还有什么...信息情报。使用您的实体及其值对象来控制和限制聚合信息。像 personidentity 可以是处理一个人的唯一性的值对象。它封装了ssn,ssn算法,处理ssn上的性别校验和等。
您的实体上的行为应反映业务模型。可以对该实体或由该实体做的事情是商业世界应该是可以对实体类或由实体类做的事情。例如:
在在线购物系统中,您可以将产品添加到购物车中。所以 Cart 类应该是这样的:
public class Cart
{
//...
public void AddProduct(Product product)
{
...code to add product to cart.
}
}
可以说方法应该反映用例。