背景
DDD领域驱动设计,在后端领域,现在应用非常广泛,在我看来,前端(特别是React)也是非常适合DDD
什么是DDD?
Domain Driven Design - 领域驱动设计
重点是这个:设计
在学习一个新的知识时候,我推荐用80%的时间去学习那些20%重要的内容 - 二八定律,Peter
领域驱动设计(DDD) 是一种通过将实现连接到持续进化的模型来满足复杂需求的软件开发方法. 领域驱动设计的前提是:
把项目的主要重点放在核心领域和领域逻辑上
把复杂的设计放在领域模型上
发起技术专家和领域专家之间的创造性协作,以迭代方式完善解决特定领域问题的概念模型
理论学完了,是不是很懵逼? 别急,上面的看不懂就假装是我拿来凑字数的,你只要记住DDD = 领域驱动设计
DDD - 举个例子
传统的四层架构如图所示:
分别为:
User Interface为用户界面层(或表示层),负责向用户显示信息和解释用户命令。这里指的用户可以是另一个计算机系统,不一定是使用用户界面的人。
Application为应用层,定义软件要完成的任务,并且指挥表达领域概念的对象来解决问题。这一层所负责的工作对业务来说意义重大,也是与其它系统的应用层进行交互的必要渠道。应用层要尽量简单,不包含业务规则或者知识,而只为下一层中的领域对象协调任务,分配工作,使它们互相协作。它没有反映业务情况的状态,但是却可以具有另外一种状态,为用户或程序显示某个任务的进度。
Domain为领域层(或模型层),负责表达业务概念,业务状态信息以及业务规则。尽管保存业务状态的技术细节是由基础设施层实现的,但是反映业务情况的状态是由本层控制并且使用的。领域层是业务软件的核心,领域模型位于这一层。
Infrastructure层为基础实施层,向其他层提供通用的技术能力:为应用层传递消息,为领域层提供持久化机制,为用户界面层绘制屏幕组件,等等。基础设施层还能够通过架构框架来支持四个层次间的交互模式。
具体示例可以看下面我画的这个图:
这是以我司的产品举例
User Interface层:为三个入口 APP PC 微信端
Application为应用层:财务系统 房源系统 合同系统等
Domain领域层:用户领域的service(接口)
Infrastructure基础实施层:用mysql redis k8s等
这样是不是很清晰了?
这四个层,你应该能分清楚了。但是,这是基于后端的分析,我们现在开始,基于前端来一份举例
前端DDD?
还是这张图
四层,对于前端进行拆分
我们这次,以掘金PC端产品举例
进行四层拆分
这里是做了一个简单的模拟拆分,大家重点关注domain领域这层,精髓在这。
为什么说我认为React很适合DDD呢?
React中,有function component(函数组件)和react hooks(而且可自定义)
领域驱动设计,说白了,重点是优化设计,但是实现的重点在于领域。所以这个领域的拆分就很有灵性了
说到这里可能有很多争议,对于我写的内容,没关系,你不喜欢别看就行了。
拿用户系统领域举例
在用户系统中,我们进入页面后,又会划分为几个模块
例如:
个人信息、最近动态、个人成就等等,这个领域每个不同的模块,像不像是我们前端的组件化?
可是大家别忘了,UI跟逻辑是要分离的。
为什么要分离?因为UI跟逻辑在前端,同样需要复用。
在我们拆分完了领域后,会得到若干不同的领域
但是在拆分领域后,我们就可以开始梳理这个领域内的逻辑了
例如,通过用户ID查询用户信息,查询用户的最近动态等
在用户模块,我们会用到通过用户ID查询用户信息这个逻辑,但是在文章模块(其他领域)同样会用到
例如:在评论系统领域,也会用到通过ID查询用户信息的逻辑
那这个逻辑,就需要复用了。在DDD的世界里,高内聚,低耦合的实现,最好是通过单纯的接口调用。例如:
A领域(评论系统)需要通过用户ID去查询用户系统,不应该直接去查询数据库,而是去调用B领域(用户系统)提供的纯粹查询接口.
那么前端可以借鉴这个思路吗?当然可以。
借鉴后端微服务思路 - 前端逻辑复用 - DDD
还是刚才的例子:在评论系统领域,也会用到通过ID查询用户信息的逻辑
首先我们定义好不同的领域
分为:user和comment 用户、评论领域
定义好获取用户信息的自定义hooks
import { useState, useEffect } from 'react';
export function UseUser({ user_id }) {
const [useInfo, setUserInfo] = useState({});
useEffect(() => {
fetch('/get-user').then((res) => {
setUserInfo(res.data);
});
}, [user_id]);
return useInfo;
}
那么当评论领域需要用到的时候,只需要找到user领域提供的这个useUser,调用一次就可以通过ID获取到用户信息了。而开发评论领域的同事,就不需要关心这个接口怎么实现的,是post还是get请求方式,接口地址是什么。
UI复用-DDD
同理,如上
举了例子,回到概念
当我们用前端项目和代码实际举例之后,再回到概念。
我个人认为 DDD最大的好处是:
接触到需求第一步就是考虑领域模型,而不是将其切割成数据和行为,然后数据用数据库实现,行为使用服务实现,最后造成需求的首肢分离。DDD让你首先考虑的是业务语言,而不是数据。重点不同导致编程世界观不同。
这句话也是我在网上看到别人总结的,觉得很对。
既然是领域驱动设计,那么我们主要的关注点理所当然应该放在如何设计领域模型上,以及对领域模型的划分。
领域并不是多么高深的概念,比如,一个保险公司的领域中包含了保险单、理赔和再保险等概念;一个电商网站的领域包含了产品名录、订单、发票、库存和物流的概念。这里,我主要讲讲对领域的划分,即将一个大的领域划分成若干个子域。
在日常开发中,我们通常会将一个大型的软件系统拆分成若干个子系统。这种划分有可能是基于架构方面的考虑,也有可能是基于基础设施的。但是在DDD中,我们对系统的划分是基于领域的,也即是基于业务的。
于是,问题也来了:首先,哪些概念应该建模在哪些子系统里面?我们可能会发现一个领域概念建模在子系统A中是可以的,而建模在子系统B中似乎也合乎情理。第二个问题是,各个子系统之间的应该如何集成?有人可能会说,这不简单得就像客户端调用服务端那么简单吗?问题在于,两个系统之间的集成涉及到基础设施和不同领域概念在两个系统之间的翻译,稍不注意,这些概念就会对我们精心创建好的领域模型造成污染。
如何解决?答案是:限界上下文和上下文映射图。
在一个领域/子域中,我们会创建一个概念上的领域边界,在这个边界中,任何领域对象都只表示特定于该边界内部的确切含义。这样边界便称为限界上下文。限界上下文和领域具有一对一的关系。
举个例子,同样是一本书,在出版阶段和出售阶段所表达的概念是不同的,出版阶段我们主要关注的是出版日期,字数,出版社和印刷厂等概念,而在出售阶段我们则主要关心价格,物流和发票等概念。我们应该怎么办呢,将所有这些概念放在单个Book对象中吗?这不是DDD的做法,DDD有限界上下文将这两个不同的概念区分开来。
从物理上讲,一个限界上下文最终可以是一个DLL(.NET)文件或者JAR(Java)文件,甚至可以是一个命名空间(比如Java的package)中的所有对象。但是,技术本身并不应该用来界分限界上下文。
将一个限界上下文中的所有概念,包括名词、动词和形容词全部集中在一起,我们便为该限界上下文创建了一套通用语言。通用语言是一个团队所有成员交流时所使用的语言,业务分析人员、编码人员和测试人员都应该直接通过通用语言进行交流。
对于上文中提到的各个子域之间的集成问题,其实也是限界上下文之间的集成问题。在集成时,我们主要关心的是领域模型和集成手段之间的关系。比如需要与一个REST资源集成,你需要提供基础设施(比如Spring 中的RestTemplate),但是这些设施并不是你核心领域模型的一部分,你应该怎么办呢?答案是防腐层,该层负责与外部服务提供方打交道,还负责将外部概念翻译成自己的核心领域能够理解的概念。当然,防腐层只是限界上下文之间众多集成方式的一种,另外还有共享内核、开放主机服务等,具体细节请参考《实现领域驱动设计》原书。限界上下文之间的集成关系也可以理解为是领域概念在不同上下文之间的映射关系,因此,限界上下文之间的集成也称为上下文映射图。
这段话来源于:www.cnblogs.com/Leo_wl/p/38…
DDD,不止适用于本文之前提到的四层架构,还有事件驱动架构,六边形架构等。(其实要完全学精,要下一番功夫。)
例如:六边形架构
所以,DDD落地的前提:对业务足够了解,在开发之前,召集所有的研发进行一次彻底的头脑风暴,把领域和子域划分清楚!想清楚再动手
最后
DDD并不是只适用于编程,在生活中同样适用,例如不同的领域需要安排不同的人工作,当你需要做某件事的时候,不是自己去学习从零开始,而是去找专业的人做专业的事。 - 这也是DDD的思想
DDD目前在国外、后端领域非常火爆,但是在我们公司,前端重构也在使用DDD,如果你感兴趣,可以去买本相关的书籍,好好学习下,然后看是否能在前端项目中落地。
❤️ 谢谢支持
以上便是本次分享的全部内容,希望对你有所帮助^_^
喜欢的话别忘了 分享、点赞、收藏 三连哦~。
欢迎我的掘金,关注公众号 前端巅峰 一手好文章~