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(...).