在企业应用程序架构模式中,Martin Fowler讨论了组织域逻辑的两种模式:Domain Model和Service Layer。域模型模式是“纯OOP”方法,其中模型(可能使用ORM从数据库中查找的那些对象)包含业务逻辑(尽管可能仅委托给另一个类中的逻辑)。
服务层模式类似于域模型模式,但前面有一个薄层,包含可以执行的业务操作。在MVC中,控制器主要与服务层交互。我相信大多数精心设计的MVC Web应用程序都使用这种模式。
现在,我的问题。 Martin建议域模型方法是面向对象的方法,因此更好。根据我的经验,在实践中实施非常困难(见:不可能)。
以上面第一张图中给出的例子为例。有两个“实体”Contract
和Product
。这些使用映射器持久保存到数据库。在示例中,有一个RecognitionStrategy
。 Martin在实体本身中提出了委托此策略的方法,该策略包含实际的业务逻辑;客户端使用contract.calculateRecognitions
或contract.recognizedRevenue(someDate)
执行此计算。在实现类似设计时,我通常将客户端界面编写为strategy.calculateRecognitions(contract)
和strategy.recognizedRevenue(contract, someDate)
。这使得服务层成为协调战略和合同所必需的。使用的具体策略注入服务。
Product
时传递策略是一种痛苦。您需要通过一个使用具体服务的工厂创建Product
,然后在创建它时将其传递给实体。 Contract
的{{1}}可以按Product
执行查询。当我们加载Product
但不打算调用Product
时,在映射器(或ORM)中贪婪地加载Contract
可能过于热心。我的方法为我们提供了更细粒度的控制,因为服务具有数据库抽象层的知识,而实体则不应该这样。我确信在实践中还有更多的痛点,我在这里没有列举过。
Martin的方法有哪些具体优势可能说服我使用纯数据模型模式?
答案 0 :(得分:6)
关于第一点,您应该在实例化Product对象时使用依赖注入。对象图构造是一个完全标记的责任,不应与您的业务逻辑混合(单一责任原则)。
关于第二点,您的供应商特性应该隐藏在数据访问层之后,您的DAO或存储库应该根据您的需要返回对象。
关于贪婪地加载Product(在关系是一对多的情况下)的关注的另一种方法是将Product DAO注入Contract对象。使用这种方法,您可以在需要时获得与合同相关的产品(可能是在内部也可以使用的吸气剂)。
当然,不存在完美的解决方案,总会有折衷方案。您作为建筑师的工作,以评估更适合您应用的方法。
根据我的个人经验,我注意到过分依赖服务类往往会产生巨大的课程,这些课程没有明确的责任,而且通常难以测试。
使用域模型方法的好处是明确区分关注点和提高可测试性。
最后,您不需要使用“纯”域模型方法。期望域模型和服务层一起使用。 域模型实体涵盖属于其边界的行为,并且服务层覆盖逻辑不属于任何域实体。
您可能会感兴趣的其他一些参考
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
此致
Emanuel Luiz Lariguet Beltrame
答案 1 :(得分:2)
域模型代表一个对象以及它的行为比贫血的更好。因为附加的行为。基本示例是,dog
可以bark
,breathe
和eat
。在服务层中,模型通过BarkHandler
和BreatheHandler
进行了增强。
UML设计模式本身支持域模型方法。 My previous answer here。对于贫血领域模型方法(服务层),很难制作UML图(类图),即使你能够创建一个,它也没有被正式接受,所以人们会有不同的解释。
从设计角度来看,服务层太“independent
”或分开。通过查看贫血域模型class
,您无法找到与域模型相关的行为(例如,保存)。您需要搜索整个项目以查找域模型的特定行为。在富域模型中,您知道域模型本身内部的行为痕迹。
富域模型的属性具有更好的访问修饰符(public,private,protected)。以及财产可见性。例如,如果您想在提交后更改状态,则可以使该属性获得public
的访问权限,但可以访问protected
。在服务层中,您需要设置public
的设置权限,或使用internal protected
欺骗它,并使提交者通过internal access
直接更改属性。但这是一个额外的复杂性。
但anemic domain model
的富域模型does not have the flexibility具有。除非使用继承,否则无法在不更改域模型的类的情况下向模型添加行为。在贫血领域模型中,您甚至可以在运行时级别交换它。