DDD聚合根是否正确使用创建对象的静态方法

时间:2018-04-09 21:23:49

标签: c# domain-driven-design aggregateroot

拥有像这样的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;
        }
    }

3 个答案:

答案 0 :(得分:2)

DDD主要是关于您的类及其属性的命名,以便它们准确地表达您的软件应该代表的业务领域部分。

它实际上并没有规定任何技术实现细节,例如事物的实例化方式。关键是类的属性和方法在域中具有含义

静态.Create()方法从域的角度来看并没有意义,但new ..()调用构造函数也没有。

您可以决定您的域实体/聚合的构造方法应为Class.Create()而不是new Class()

不是&#34;错误&#34;

例如,如果您使用的框架/库需要一个公共无参数默认构造函数(与EntityFramework和一些序列化库等相似),并且您需要&#34;标准&#34;实例化的方法是明确的(只能用所有需要的参数调用),这是完成它的一种非常有效的方法。

只是保持一致

如果您这样做,则应该为所有实体/聚合执行此操作。它成为一种约定,它将被记录为技术实现细节(就像默认情况下的构造函数一样)。

使用CommandHandlers,而不是服务(或工厂)

服务并非真正用于实例化类(这是工厂的用途)。

但是,您的代码似乎暗示您还在应用EventSourcing或其中的一些变体。假设您还在进行CQRS,那么您的Create命令最终会以CommandHandlers Create方式结束。如果需要,您可以在此处放置验证逻辑等内容。

所以那些CommandHandler已经是你的工厂了,你不需要额外的一层。

最后:

使用类似的静态实例化方法通常用于实现某种类型的Fluent API,例如使用Builder模式。它为链接呼叫提供语法糖。

因此,它可能会给其他开发者/消费者一种错误的印象,即可以将调用链接起来(在您的示例中它不能)。

您可能仍然希望定义默认的公共构造函数,以便您可以使用文档注释来不使用该注释,并且.Create()上有一个注释应该用作构造函数。

所有这些事情都考虑过,它还是比使用构造函数更好吗?然后一定要做。它不会影响&#34; DDD-ness&#34;据我所知。

答案 1 :(得分:2)

  

拥有像这样的Create方法是否正确。

总的来说,这很好。

Udi Dahan已经注意到&#34;创造&#34;和&#34;新&#34;通常不是域语言的一部分,因此他提供了建议Don't Create Aggregate Roots

  

客户不仅仅是凭空出现。

也就是说,您的域行为应准确描述如何将新信息引入系统。

构造函数和使用代码之间的额外间接层可以减少不必要的耦合 - 命名构造函数,工厂方法和返回接口的构建器使您可以灵活地更改或修饰返回的对象。但这是一个普遍的模块化/关注点分离原则,而不是特定于域驱动设计。

答案 2 :(得分:0)

我会使用更清楚地说明您正在做什么的内容,而不是Create。您好像正在网站上注册员工。试着用措辞表达出来,好像将要使用该系统的人会说。

如果他们说“嘿,您将创建此用户”。然后,您的方法就可以了。

如果他们说:“嘿,您可以在我们的网站上注册此员工吗?”然后,您应该使用:

public static Employee RegisterOnWebsite(string name, string lastname, string email, string password, string employeeNumber, Site site)
{
    // Creation Code
}

由于您拥有员工编号作为财产。看来此用户将永远是雇员。您应该对此有所了解并以此命名。现在,您的方法将显示为Employee.RegisterOnWebsite(...)