转载自前端备忘录-江湖术士

好了,大家如果已经熟悉了 React DDD,那么可以来看看如何真正发挥出 React DDD 的威力吧!

首先,领域驱动是面向对象在自顶向下设计中,发挥最大威力的模式,是极限的面向对象思想方法

你可能会说,不对,React 是函数式

朋友,且不说 React 与极限管道风格的 cycle 等框架有多少差距,世界上存在纯粹无副作用的函数式系统么?(lambda机已经不存在了)

既然不存在,项目越大,副作用越多,自底向上的开发模式早晚会碰到它的边界,这时候,面向对象在顶层架构发威,一定会成为必然

关于为什么到了某个层级,领域驱动是必然,推荐一下阿里的回答:

软件系统从来都不是凭空而来,而是以软件的形式解决特定的问题。当我们面临现实世界的复杂问题时,如何以软件的形式落地?领域驱动设计是一套方法论,指导我们将复杂问题进行拆分、拆分出各个子系统间的关联以及是如何运转的,帮助我们解决大型的复杂系统在落地中遇到的问题

分治是 DDD 的核心

注意,这个分治,不是只分治状态,只分治状态没有意义

而是要将 状态,逻辑,资源(图片,音视频),配置项等等 一起分治

极端一点,分开开发,分开测试,分开重构,甚至分开部署

先丢掉 React 无 状态逻辑复用 (16.8 版本-)时的状态管理分层方案,那是特殊时期的过度方案,并非最佳实践

接下来,我们先思考一个问题 —— 功能逻辑(领域)该如何进行拆分?

是的,回忆一下大家在大学所学课程《软件设计概论》,想想在宏观上,我们应该如何拆分一个应用?

  • 先从用户角度,画出用例图,直接表达一般需求
  • 活动图,描述一个具体的操作流程
  • 状态图,秒速某些状态如何在一个活动中进行变化?
  • 交互图,描述对象之间的关系以及对象之间的信息传递,包括 顺序,时序,通讯,概览 四种图

其实图是其次,不同的应用方向有不同的画图标准,甚至每家公司都不一样

比如 BPMN2.0 泳道时序图,就将用例,活动,通讯都表达:

BPMN2.0 泳道时序图

状态和数据结构的定义权限交给程序员

于是乎,很多时候,只需要产品经理给你一张图,你就能完成领域设计

但是呢,现在市场上的产品经理水品经不起推敲,这部分工作他们能不能做,是个问题

所以,程序员只能自己来了

注意,画图只是辅助,你要是能不画图就能想清楚,也可以不画
领域驱动只是强制你想清楚这部分问题,没想清楚你也可以先下手写代码,但是,没有想清楚逻辑就下手,非常危险,问题在开始处暴露永远比在开发过程中暴露容易结局

然后,我们进行以下分析:

  • 抓主体:活动的发起者是谁?会对谁造成影响?
  • 抓流程:流程会经过哪些节点?错误如何处理?如果优化一下,让它有更好的体验?
  • 抓通讯:流程涉及哪些节点的通讯?如何通讯?
  • 抓中间状态:应该用什么样的数据结构,表征存储这些通讯和流程?
  • 抓边界:了解这次活动的边界在哪里,如果才能做到封装?

好了,接下来我们一步一步来:

抓主体

那些节点参与了这次活动?

产品会告诉你,用户通过了哪个按钮,操作,然后影响了哪些页面(他如果不告诉你你也要问,如果活动图没有表现出来,不然你这些都不清楚,怎么开始开发?)

找到了这些主题,你就能明白这次逻辑大概会在哪个范围内发生

比如上图中,lab,表示这次活动主要发生在 lab 模块中,哪些地方用到了 lab 模块,自己心里要有数(树形结构中,lab 模块以下层级都用到了 lab 模块)

比如:

Lab 参与了活动,是不是要变更 useLabService ?

影响了 PerformTest 组件,是否需要改变 usePerformTestService 服务?

抓流程

途中,经历了:PerformTests,ProcessResults,NotifyClinician,Supervisor contact Clinician 四个节点

这些节点是名词还是动词还是短语?

名词的话,有没有相关组件或者模块?没有的话就创建,有的话,看看在哪里埋下交互点和逻辑处理比较合适?

动词或者短语的话,是不是需要进行逻辑处理?该设计怎样的处理管道?

这个流程够不够细致(细致的话,中间结点没有动词和短语,只有名词)

不够,询问产品,把问题搞清楚

比如:

Notify Clinician ? getMessage -> request -> checkIfEnableRequest -> NoticeSupervisor?

抓通讯

那些地方存在交互?这个交互是本地的还是跨平台的(后端接口)?

能不能缩减一部分?(比如用户数据是在登入系统即会获取的)

通讯需不需要重新尝试或者有其他什么逻辑?(rxjs retry 或者 ahooks useDebounce?有些时候这部分产品不会详写)

通讯的格式是怎样的的?(问问后端?或者设计 graphql query)

一个 Notice SuperVisor 啥意思 ?是发邮件还是页面解决?

页面解决的话,显示结果的地方在哪里?

抓中间状态

明确了主体,流程,和通讯,需要思考用什么样的状态格式来进行存储,方便调试和重构

重点分析那些箭头,怎么样用数据表征这些箭头,同时还需要知会后端(devApi,swagger 等)

有没有统一通讯功能?可以直接复用?

比如:

request LabRequest , request Stat,Notice SuperVisor ,开始,结束

是否需要同一个结构?

{name:” lab request “,status:”stat request”, handler:”xxx”} 诸如此类?

抓边界

好了,最后仔细思考,要在哪些文件变更,依赖应该放置于哪些层级,之前的依赖要不要动手进行提升?

好了,这部分 labModule 和 supervisorModule 都用到了,之前 labService 在 labModule 上,我现在需要提升到 appModule

okay,移动文件夹,修改注入点

接下来,就是一阵操作猛如虎,一个模块一个模块实现~

最后

至于数据结构方面,由于摒弃了 this 和继承,没有必要纠结数据结构

但是要明白流程在什么范围操作数据结构,必要的时候做服务聚焦

最后,开发完成之后,用类似 Typescript UML 之类的工具做验证,看看自己是否如此实现的

再用 typeDoc,直接把项目文档 load 出来,再运行一下测试,给出测试报告

this is all over~

还是那句话

80% 的时间花来做分析和设计,能让你剩下的工作效率提高十倍以上

写代码的时候,脑袋活跃的区域是语言区,你是真不可能在那种情况下实现复杂逻辑的!

所以还是老老实实先把业务分析清楚吧

领域驱动,换一个词语,就是业务驱动!

至于什么渲染啊,性能啊之类的东西,不是你该考虑的问题,在 新版 react/Vue 和 Angular 支持下,这些问题交给框架解决~