15

在企业应用架构模式中,Martin Fowler 谈到了组织领域逻辑的两种模式:领域模型服务层。领域模型模式是“纯 OOP”方法,其中模型(可能使用 ORM 从数据库中查找的那些对象)包含业务逻辑(尽管可能只委托给另一个类中的逻辑)。

示例域模型

服务层模式类似于域模型模式,但在它前面有一个薄层,包含可以执行的业务操作。在 MVC 中,控制器主要与服务层交互。我相信大多数设计良好的 MVC Web 应用程序都使用这种模式。

示例服务层

现在,我的问题。Martin 建议域模型方法是更面向对象的方法,因此更好。以我的经验,在实践中实施非常困难(参见:不可能)。

以上面第一张图中给出的例子为例。有两个“实体”ContractProduct。这些通过映射器持久化到数据库中。在示例中,有一个RecognitionStrategy. Martin 将委托给包含实际业务逻辑的策略的方法放在实体本身中;contract.calculateRecognitions客户端使用or执行此计算contract.recognizedRevenue(someDate)。在实现类似的设计时,我通常将客户端界面编写为strategy.calculateRecognitions(contract)strategy.recognizedRevenue(contract, someDate)。这使得服务层对于协调策略和合同是必要的。使用的具体策略被注入到服务中。

从设计的角度来看,Martin 的方法肯定更具吸引力,但围绕设置的工作要困难得多:

  1. 在实例化 a 时传递策略Product是一种痛苦。您需要Product通过带有要使用的具体服务的工厂来创建 s,然后在创建实体时将其传递给实体。
  2. 对数据库访问的细粒度控制较少。根据 ORM 设置,Contract委托给Product每个Product. 当我们加载ProductaContract但不打算调用contract.calculateRecognitions(). 我的方法为我们提供了更细粒度的控制,因为服务具有数据库抽象层的知识,而实体不应该。

我敢肯定,在实践中还有更多的痛点我在这里没有一一列举。

Martin 的方法有哪些具体优势可以说服我使用纯数据模型模式?

4

2 回答 2

6

关于您的第一点,您应该在实例化 Product 对象时使用依赖注入。对象图构建是一项完全标记的责任,不应与您的业务逻辑混合(单一责任原则)。

关于第二点,您的供应商特殊性应该隐藏在您的数据访问层之后,并且您的 DAO 或存储库应该根据您的需要返回对象。

您担心贪婪地加载 Product s(在关系是一对多的情况下)的另一种方法是将 Product DAO 注入到 Contract 对象中。使用这种方法,您可以在需要时获得与合同相关的 Product (可能在也可以在内部使用的 getter 上)。

当然,完美的解决方案并不存在,总会有取舍。您作为架构师的工作是评估更适合您的应用程序的方法。

根据我的个人经验,我注意到过度依赖服务类往往会生成巨大的类,这些类没有明确的职责并且通常难以测试。

因此,使用领域模型方法的好处是关注点的清晰分离和可测试性的提高。

最后,您不需要使用“纯”域模型方法。领域模型和服务层有望一起使用。领域模型实体涵盖了属于其边界的行为,而服务层涵盖的逻辑不属于任何领域实体。

您可能会发现一些额外的参考资料很有趣

Domain Driven Design and Development In Practice - 一篇关于 DDD 的有趣文章

Dependency Injection, Design patterns using Spring and Guice - 关于依赖注入的好书

问候,

伊曼纽尔·路易斯·拉里盖特·贝尔特拉姆

于 2015-04-23T00:11:43.037 回答
2

域模型比贫血模型更好地表示一个对象及其行为。因为行为依附于它。基本示例是a dogcanbark和。在服务层,模型通过和增强。breatheeatBarkHandlerBreatheHandler

UML 设计模式原生支持领域模型方法。我之前的回答在这里。对于贫血的领域模型方法(服务层),很难制作 UML 图(类图),即使你能创建一个,它也没有被官方接受,所以人们会有不同的解释。

从设计角度来看,服务层过于“ independent”或分离。通过查看贫血域模型class,您无法找到与域模型相关的行为(例如保存)。您需要搜索整个项目以找到域模型的特定行为。在富域模型中,您知道域模型本身内部的行为痕迹。

富域模型的属性具有更好的访问修饰符(公共、私有、受保护)。以及属性可见性。例如,如果您想在提交后更改状态,您可以使属性获得访问权限public,但设置访问权限protected。在服务层,你需要让set访问public,或者欺骗它,internal protected让提交者直接通过internal access。但这是一个额外的复杂性。

但富域模型没有那样的灵活性anemic domain model。除非您使用继承,否则您无法在不更改域模型的类的情况下向模型添加行为。在贫血域模型中,您甚至可以在运行时级别交换它。

于 2014-03-03T01:29:21.450 回答