工厂模式应该在哪里生活DDD?

时间:2012-12-10 16:16:11

标签: c# design-patterns domain-driven-design architectural-patterns

我已经讨论了一段时间了,但仍然没有得出结论。 虽然我看到大多数示例都在应用程序层中有工厂代码,但我倾向于认为它应该在域层中。 原因: 我有时会在我的工厂进行初始验证,我希望所有对象的创建都能通过。 我希望此代码可用于我的对象的所有实例化。 有时,操作需要感觉不自然的参数信息传递给构造函数。 还有一些不那么重要的原因。

有没有理由说这是一种不好的做法? 这会破坏其他模式吗?

5 个答案:

答案 0 :(得分:8)

DDD中的工厂只是factory pattern的一个实例,因此它应该在最有意义的地方使用。要考虑的另一个原则是information expert模式,它基本上表明应该将行为分配给最接近信息的类。因此,如果您要实施某些特定于域的规则和逻辑,请将工厂放在域层中 - 毕竟,工厂会创建域对象。但请注意,您可能在其他层中有其他类型的工厂。

答案 1 :(得分:6)

从内存中,Eric Evans的书中有一些例子,其中对象工厂是域层的一部分。

对我而言,将工厂放在这里是完全合理的。

答案 2 :(得分:4)

+1这样做。可访问性是一个很好的理由,我会保持创建代码至少接近域模型层。否则,域模型的用户将在查找受限制的访问构造函数时特别混淆如何实例化它。实际上,将它分开的一个合理的理由是你有不同的有效方法来创建相同的东西,例如通常在使用抽象工厂时就是这种情况。

如果我必须把它分开,我会把它放在例如一个软件包(在Java的情况下)至少与域模型的级别相同,并且总是与它一起发送,例如。

upper
  --> domain
  --> domain_factory

答案 3 :(得分:1)

我更喜欢应用层中的工厂。

如果您将工厂保留在域层中,当您需要复杂类型作为参数时,它们将无法帮助您(C#代码示例):

Application Layer:

//this Factory resides in the Domain Layer and cannot reference anything else outside it
Person person = PersonAggregateFactory.CreateDeepAndLargeAggregate(
            string name, string code, string streetName,...
            and lots of other parameters...);

//these ones reside in Application Layer, thus can be much more simple and readable:
Person person = PersonAggregateFactory.CreateDeepAndLargeAggregate(CreatePersonCommand);
Person person = PersonAggregateFactory.CreateDeepAndLargeAggregate(PersonDTO);



Domain Layer:

public class Person : Entity<Person>
{
    public Address Address {get;private set;}
    public Account Account {get;private set;}
    public Contact Contact {get;private set;}
    public string Name {get;private set;}

    public Person(string name, Address address,Account account, Contact contact)
    {
        //some validations & assigning values...
        this.Address = address;
        //and so on...

    }

}

public class Address:Entity<Address>{
    public string Code {get;private set;}
    public string StreetName {get;private set;}
    public int Number {get;private set;}
    public string Complement {get;private set;}
    public Address(string code, string streetName, int number, string complement?)
    {
        //some validations & assigning values...
        code = code;
    }

}

public class Account:Entity<Account>{
    public int Number {get;private set;}

    public Account(int number)
    {
        //some validations & assigning values...
        this.Number = number;
    }

}

//yout get the idea:
//public class Contact...

此外,没有义务将工厂保留在域层内(来自Domain Driven Design Quickly):

  

因此,转移创建复杂实例的责任   对象和聚合到单独的对象,本身可以拥有   在域模型中没有责任,但仍然属于   域名设计。提供封装所有复杂的界面   程序集,并不需要客户端引用   正在实例化的对象的具体类。创建整个   聚合为一个单元,强制执行不变量。

由于我没有使用工厂将持久对象加载到内存中,因此不必从应用程序以外的其他层访问它们。这就是为什么(来自Domain Driven Design Quickly):

  

另一个观察是,工厂需要创建新对象   从头开始,或者他们需要重建那些对象   以前存在,但可能已经坚持到了   数据库。 将实体从休息状态带回记忆中   在数据库中放置一个完全不同的过程   创建一个新的。一个明显的区别是新的   对象不需要新的身份。该对象已经有一个。   违反不变量的方式不同。 当一个新的   对象是从头开始创建的,任何违反不变量的行为都将结束   异常。对于从中重新创建的对象,我们无法做到这一点   数据库。对象需要以某种方式修复,所以他们   可以正常运行,否则会有数据丢失。

答案 4 :(得分:0)

如果构建器/工厂仅依赖于域类和基元,请将它们放在域层中,否则将它们放在域层之外。

相关问题