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

ret*_*lig 9 c# design-patterns domain-driven-design architectural-patterns

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

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

eul*_*rfx 10

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


Dav*_*rne 7

从记忆中,Eric Evans的书中有一些例子,其中对象工厂是域层的很大一部分.

对我来说,在这里找到你的工厂是完全合理的.


Sky*_*ker 6

为此+1。可访问性将是一个很好的理由,我将使创建代码至少与域模型层保持一致。否则,域模型的用户将很困惑如何在查找受限制的访问构造函数时特别地实例化它。实际上,将其分开的一个合理原因是您有不同的有效方法来创建相同的事物,例如,在采用Abstract Factory时通常就是这种情况。

如果必须将其分开,则可以将其放入至少与域模型相同级别的包中(对于Java),并始终将其与之一起发送。

upper
  --> domain
  --> domain_factory
Run Code Online (Sandbox Code Playgroud)


Ale*_*der 6

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

\n\n

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

\n\n
Application Layer:\n\n//this Factory resides in the Domain Layer and cannot reference anything else outside it\nPerson person = PersonAggregateFactory.CreateDeepAndLargeAggregate(\n            string name, string code, string streetName,...\n            and lots of other parameters...);\n\n//these ones reside in Application Layer, thus can be much more simple and readable:\nPerson person = PersonAggregateFactory.CreateDeepAndLargeAggregate(CreatePersonCommand);\nPerson person = PersonAggregateFactory.CreateDeepAndLargeAggregate(PersonDTO);\n\n\n\nDomain Layer:\n\npublic class Person : Entity<Person>\n{\n    public Address Address {get;private set;}\n    public Account Account {get;private set;}\n    public Contact Contact {get;private set;}\n    public string Name {get;private set;}\n\n    public Person(string name, Address address,Account account, Contact contact)\n    {\n        //some validations & assigning values...\n        this.Address = address;\n        //and so on...\n\n    }\n\n}\n\npublic class Address:Entity<Address>{\n    public string Code {get;private set;}\n    public string StreetName {get;private set;}\n    public int Number {get;private set;}\n    public string Complement {get;private set;}\n    public Address(string code, string streetName, int number, string complement?)\n    {\n        //some validations & assigning values...\n        code = code;\n    }\n\n}\n\npublic class Account:Entity<Account>{\n    public int Number {get;private set;}\n\n    public Account(int number)\n    {\n        //some validations & assigning values...\n        this.Number = number;\n    }\n\n}\n\n//yout get the idea:\n//public class Contact...\n
Run Code Online (Sandbox Code Playgroud)\n\n

此外,没有义务将工厂保留在域层内(来自快速域驱动设计):

\n\n
\n

因此,将创建复杂对象和聚合实例的责任转移到一个单独的对象,该对象本身可能在域模型中不承担任何责任,但仍然是域设计的一部分。提供一个封装所有复杂程序集的接口,并且不需要客户端引用正在实例化的对象的具体类。创建整个聚合作为一个单元,强制执行它们的不变量。

\n
\n\n

由于我不使用工厂将持久对象加载到内存中,因此不必从应用程序之外的其他层访问它们。原因如下(来自快速领域驱动设计):

\n\n
\n

另一个观察结果是,工厂需要从头开始创建新对象,或者需要重构以前存在但可能已保存到数据库中的对象。将实体从数据库中的静止位置带回内存涉及与创建新实体完全不同的过程。一个明显的区别是新对象不需要新的标识。该对象已经有一个。\n 违反不变量的情况会受到不同的处理。当从头开始创建新对象时,任何违反不变量的行为都会导致异常。我们可以\xe2\x80\x99t 使用从数据库重新创建的对象来做到这一点。这些对象需要以某种方式修复,以便它们可以正常运行,否则就会丢失数据。

\n
\n