设计模式之美 -- 充血模型的 DDD 开发模式例子
DDD is nothing more than OOP applied to business models.
DDD 其实就是把 OOP 应用于业务模型。
其他层和贫血模型都一样,service 层实现:
问题 1:在基于充血模型的 DDD 开发模式中,将业务逻辑移动到 Domain 中,Service 类变得很薄,但在我们的代码设计与实现中,并没有完全将 Service 类去掉,这是为什么?或者说,Service 类在这种情况下担当的职责是什么?哪些功能逻辑会放到 Service 类中?
目的:
Service 类负责一些不适合放在 Domain 类中的功能。比如,负责与 Repository 层打交道、跨领域模型的业务聚合功能、幂等事务等非功能性的工作。
Service 类负责与 Repository 交流。之所以让 VirtualWalletService 类与 Repository 打交道,而不是让领域模型 VirtualWallet 与 Repository 打交道,那是因为我们想保持领域模型的独立性,不与任何其他层的代码(Repository 层的代码)或开发框架(比如 Spring、MyBatis)耦合在一起,将流程性的代码逻辑(比如从 DB 中取数据、映射数据)与领域模型的业务逻辑解耦,让领域模型更加可复用
Service 类负责跨领域模型的业务聚合功能。VirtualWalletService 类中的 transfer() 转账函数会涉及两个钱包的操作,因此这部分业务逻辑无法放到 VirtualWallet 类中,所以,我们暂且把转账业务放到 VirtualWalletService 类中了。当然,虽然功能演进,使得转账业务变得复杂起来之后,我们也可以将转账业务抽取出来,设计成一个独立的领域模型。
问题 2:在基于充血模型的 DDD 开发模式中,尽管 Service 层被改造成了充血模型,但是 Controller 层和 Repository 层还是贫血模型,是否有必要也进行充血领域建模呢?
没必要。
包含的业务逻辑并不多,必要做充血建模,否则过度设计。
Repository 层的 Entity:虽然贫血模型有容易被到处修改的风险,但是因为生命周期短,所以不存在这种情况。
Controller 层的 VO(view object)。实际上 VO 是一种 DTO(Data Transfer Object,数据传输对象)。从功能上来讲,它理应不包含业务逻辑、只包含数据。
如何理解 DDD:
为了降低软件复杂度,让软件容易维护。
理解 OOP,我们就不难理解 DDD:
DDD 第一原则:将数据和操作结合。(贫血模型将数据和操作分离,违反 OOP 的原则。)(封装)
DDD 第二原则:界限上下文。这是将“单一职责”应用于我们的领域模型。 (还是封装)
实现方法:
使用通用语言(Ubiquitous Language):类、方法、字段的命名,要符合业务。使用业务语言命名,以后在和客户或者其他团队交流时能够更顺畅。
理解系统业务
DDD 中的 Service 层
1. Service 层只是一个中间层,起到连接和组合作用。 用于支持领域模型层和 Repository 层的交互(连接作用),利用各种领域对象执行业务逻辑(组合作用)。 比如通过 Repository 查出数据,将数据转换为领域模型对象,利用领域模型对象执行业务逻辑(核心),然后调用 Repository 更新领域模型中的数据。
2. Service 类还负责一些非功能性及与三方系统交互的工作。 比如幂等、事务、发邮件、发消息、记录日志、调用其他系统的 RPC 接口等。
不允许 Service 中的逻辑过于复杂,如果 Service 中的组合的业务逻辑过于复杂,我们就要将这业务逻辑抽取出一个新的领域对象进行封装,通过调用这个领域对象来进行这些复杂的操作。
版权声明: 本文为 InfoQ 作者【GalaxyCreater】的原创文章。
原文链接:【http://xie.infoq.cn/article/46a71c9a019c2963d75966857】。文章转载请联系作者。
评论