【问题标题】:Domain Model and Service Layer patterns in P of EAAEAA P 中的领域模型和服务层模式
【发布时间】:2014-04-03 15:54:39
【问题描述】:

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

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

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

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

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

  1. 在实例化Product 时传递策略是一种痛苦。您需要通过带有要使用的具体服务的柯里化工厂创建Products,然后在创建实体时将其传递给实体。
  2. 对数据库访问的细粒度控制较少。根据 ORM 设置,委派给ProductContract 可能会根据Product 执行查询。当我们加载Contract 但不打算调用contract.calculateRecognitions() 时,在映射器(或ORM)中贪婪地加载Products 可能过于热心了。我的方法为我们提供了更细粒度的控制,因为服务了解数据库抽象层,而实体不应该这样做。

我确信在实践中还有更多我没有在这里列举的痛点。

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

【问题讨论】:

    标签: design-patterns service-layer architectural-patterns anemic-domain-model


    【解决方案1】:

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

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

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

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

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

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

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

    一些您可能会感兴趣的额外参考资料

    Domain Driven Design and Development In Practice - An interesting article on DDD

    Dependency Injection, Design patterns using Spring and Guice - Great book on dependency injection

    问候,

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

    【讨论】:

      【解决方案2】:

      域模型比贫血模型更好地代表一个对象以及它的行为。因为行为依附于它。基本示例是,dog 可以是 barkbreatheeat。在服务层,模型通过BarkHandlerBreatheHandler进行了增强。

      UML 设计模式原生支持域模型方法。 My previous answer here。对于贫血的领域模型方法(服务层),很难制作 UML 图(类图),而且即使你能制作出来,它也没有被官方接受,所以人们会有不同的解释。

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

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

      但富域模型does not have the flexibilityanemic domain model 一样。除非您使用继承,否则您不能在不更改域模型的类的情况下向模型添加行为。在贫血域模型中,您甚至可以在运行时级别交换它。

      【讨论】:

        猜你喜欢
        • 2014-02-15
        • 2012-02-04
        • 2014-02-24
        • 1970-01-01
        • 2015-12-30
        • 2012-04-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多