DDD聚合根有一个创建对象的静态方法是否正确
DDD aggregate root is it correct to have an static method that create the object
这样的Create方法是否正确。或者我应该在服务中创建用户。这会破坏 DDD 概念吗?
对于这种情况,最佳做法是什么?
注:我也在用DI。
public class User : HistoryBase, IAggregateRoot
{
private IEnumerable<Role> _roles = new List<Role>();
public string Name { get; protected set; }
public string Lastname { get; protected set; }
public string Email { get; protected set; }
public string Password { get; protected set; }
public string EmployeeNumber { get; protected set; }
public bool Active { get; protected set; }
public int SiteID { get; protected set; }
public IEnumerable<Role> Roles { get { return _roles; } }
public static User Create(string name, string lastname, string email, string password, string employeeNumber, Site site)
{
var user = new User()
{
Name = name,
Lastname = lastname,
Email = email,
Password = password,
EmployeeNumber = employeeNumber,
SiteID = site.ID
};
return user;
}
}
DDD 主要是关于 命名 您的 classes 及其属性,以便它们准确地表达您的软件应该代表的业务领域部分.
它确实没有规定任何技术实现细节,例如如何实例化事物。重点是 class 上的属性和方法在域中具有 含义 。
静态 .Create()
方法从领域的角度来看没有任何意义,但对构造函数的 new ..()
调用也没有意义。
您可以决定您的域 entities/aggregates 的构造方法应为 Class.Create()
而不是 new Class()
。
不是"wrong"
例如,如果您使用的 frameworks/libraries 需要一个 public 无参数默认构造函数(常见于 EntityFramework 和一些序列化库)并且您想要 "standard" 实例化方法是明确的(只能用所有需要的参数调用),这是实现这一点的完全有效的方法。
保持一致
如果你这样做,你应该为你所有的 entities/aggregates 这样做。它成为一种约定,并将被记录为技术实现细节(就像默认情况下构造函数一样)。
使用 CommandHandlers,而不是服务(或工厂)
服务并不是真正用于实例化 classes(这就是工厂的用途)。
但是,您的代码似乎暗示您也在应用 EventSourcing 或其某些变体。假设您随后也在执行 CQRS,您有 Create
命令在您的 CommandHandlers Create
方法中结束。如果需要,您可以在此处放置诸如验证逻辑之类的东西。
所以那些 CommandHandlers 已经是你的工厂了,你不需要额外的层。
最后:
使用像这样的静态实例化方法通常用于实现某种 Fluent API,例如使用 Builder 模式。它为链接调用提供语法糖。
因此它可能会给其他开发人员/消费者一种错误的印象,即调用可以链接(在您的示例中不能)。
您可能仍然希望定义默认的 public 构造函数,这样您就可以在文档注释中不使用那个构造函数,并且在 .Create()
上有一个应该用作构造函数。
考虑到所有这些,它是否仍然比仅使用构造函数更好?然后一定要去做。据我所知,它不会影响 "DDD-ness"。
Is it correct to have a Create method like this.
总的来说,没问题。
Udi Dahan 注意到 "create" 和 "new" 通常不是域语言的一部分,因此他提供了建议 Don't Create Aggregate Roots
Customers don’t just appear out of thin air.
也就是说,您的领域行为应该准确地描述新信息是如何被引入系统的。
构造函数和使用代码之间的额外间接层可以减少不必要的耦合 - return 接口的命名构造函数、工厂方法和构建器使您可以灵活地更改或装饰您 return。但这是一般的模块化/关注点分离原则,并不特定于领域驱动设计。
我会选择更清楚地说明你在做什么的东西,而不是 Create
。看起来您正在网站上注册员工。试着像将要使用该系统的人那样说。
如果他们说,"Hey, will you create this user."那么,你的方法就可以了。
如果他们说,"Hey, will you register this employee on our website?" 那么,你应该使用:
public static Employee RegisterOnWebsite(string name, string lastname, string email, string password, string employeeNumber, Site site)
{
// Creation Code
}
由于您的员工编号为 属性。看起来这个用户永远是雇员。你应该清楚这一点并这样命名。现在,您的方法将类似于 Employee.RegisterOnWebsite(...)
.
这样的Create方法是否正确。或者我应该在服务中创建用户。这会破坏 DDD 概念吗?
对于这种情况,最佳做法是什么?
注:我也在用DI。
public class User : HistoryBase, IAggregateRoot
{
private IEnumerable<Role> _roles = new List<Role>();
public string Name { get; protected set; }
public string Lastname { get; protected set; }
public string Email { get; protected set; }
public string Password { get; protected set; }
public string EmployeeNumber { get; protected set; }
public bool Active { get; protected set; }
public int SiteID { get; protected set; }
public IEnumerable<Role> Roles { get { return _roles; } }
public static User Create(string name, string lastname, string email, string password, string employeeNumber, Site site)
{
var user = new User()
{
Name = name,
Lastname = lastname,
Email = email,
Password = password,
EmployeeNumber = employeeNumber,
SiteID = site.ID
};
return user;
}
}
DDD 主要是关于 命名 您的 classes 及其属性,以便它们准确地表达您的软件应该代表的业务领域部分.
它确实没有规定任何技术实现细节,例如如何实例化事物。重点是 class 上的属性和方法在域中具有 含义 。
静态 .Create()
方法从领域的角度来看没有任何意义,但对构造函数的 new ..()
调用也没有意义。
您可以决定您的域 entities/aggregates 的构造方法应为 Class.Create()
而不是 new Class()
。
不是"wrong"
例如,如果您使用的 frameworks/libraries 需要一个 public 无参数默认构造函数(常见于 EntityFramework 和一些序列化库)并且您想要 "standard" 实例化方法是明确的(只能用所有需要的参数调用),这是实现这一点的完全有效的方法。
保持一致
如果你这样做,你应该为你所有的 entities/aggregates 这样做。它成为一种约定,并将被记录为技术实现细节(就像默认情况下构造函数一样)。
使用 CommandHandlers,而不是服务(或工厂)
服务并不是真正用于实例化 classes(这就是工厂的用途)。
但是,您的代码似乎暗示您也在应用 EventSourcing 或其某些变体。假设您随后也在执行 CQRS,您有 Create
命令在您的 CommandHandlers Create
方法中结束。如果需要,您可以在此处放置诸如验证逻辑之类的东西。
所以那些 CommandHandlers 已经是你的工厂了,你不需要额外的层。
最后:
使用像这样的静态实例化方法通常用于实现某种 Fluent API,例如使用 Builder 模式。它为链接调用提供语法糖。
因此它可能会给其他开发人员/消费者一种错误的印象,即调用可以链接(在您的示例中不能)。
您可能仍然希望定义默认的 public 构造函数,这样您就可以在文档注释中不使用那个构造函数,并且在 .Create()
上有一个应该用作构造函数。
考虑到所有这些,它是否仍然比仅使用构造函数更好?然后一定要去做。据我所知,它不会影响 "DDD-ness"。
Is it correct to have a Create method like this.
总的来说,没问题。
Udi Dahan 注意到 "create" 和 "new" 通常不是域语言的一部分,因此他提供了建议 Don't Create Aggregate Roots
Customers don’t just appear out of thin air.
也就是说,您的领域行为应该准确地描述新信息是如何被引入系统的。
构造函数和使用代码之间的额外间接层可以减少不必要的耦合 - return 接口的命名构造函数、工厂方法和构建器使您可以灵活地更改或装饰您 return。但这是一般的模块化/关注点分离原则,并不特定于领域驱动设计。
我会选择更清楚地说明你在做什么的东西,而不是 Create
。看起来您正在网站上注册员工。试着像将要使用该系统的人那样说。
如果他们说,"Hey, will you create this user."那么,你的方法就可以了。
如果他们说,"Hey, will you register this employee on our website?" 那么,你应该使用:
public static Employee RegisterOnWebsite(string name, string lastname, string email, string password, string employeeNumber, Site site)
{
// Creation Code
}
由于您的员工编号为 属性。看起来这个用户永远是雇员。你应该清楚这一点并这样命名。现在,您的方法将类似于 Employee.RegisterOnWebsite(...)
.