Skip to content

Commit

Permalink
add doc
Browse files Browse the repository at this point in the history
  • Loading branch information
entropy-cloud committed Jul 21, 2023
1 parent 746884c commit 0d6f787
Showing 1 changed file with 78 additions and 17 deletions.
95 changes: 78 additions & 17 deletions docs/theory/pros-and-cons-of-framework.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,68 +6,67 @@
> 关于NopORM引擎的理论分析,可以参见 [低代码平台需要什么样的ORM引擎?(1)](https://zhuanlan.zhihu.com/p/543252423)[低代码平台需要什么样的ORM引擎?(2)](https://zhuanlan.zhihu.com/p/545063021)

# 朴素的评判标准

很多人对于框架技术的评判标准是很朴素的,比如:

1. **使用XML这种被淘汰的技术作为信息载体,差评!**
根据某种局部的技术表现形式来判断一种框架技术的优劣无疑是片面化、表面化的,一种创新性技术的本质是提供了一种新的逻辑组织机制,这种机制具体以什么样的技术形式作为表观载体是一个次要性的问题。
根据某种局部的技术表现形式来判断一种框架技术的优劣无疑是片面化、表面化的,一种创新性技术的本质是提供了一种新的逻辑组织机制,这种机制具体以什么样的技术形式作为表观载体是一个次要性的问题。

2. **用的人多吗?文档详细吗?**
这是最实用主义的一种观点。但它评价的是对我这种拿来主义的人而言,这个框架是否容易使用的问题,而不是这个框架本身的设计优劣问题。
这是最实用主义的一种观点。但它评价的是对我这种拿来主义的人而言,这个框架是否容易使用的问题,而不是这个框架本身的设计优劣问题。

3. **用着方便,容易理解**
这里表达的其实是一种主观感受,而不是可以客观衡量的判据。在不同的技术环境、技术背景下,每个人有着不同的技术偏好,个体感受可能存在着巨大差异。
这里表达的其实是一种主观感受,而不是可以客观衡量的判据。在不同的技术环境、技术背景下,每个人有着不同的技术偏好,个体感受可能存在着巨大差异。

# 存在客观的评判标准吗?

软件开发虽然是一种实践性很强的技术活动,但是在科学昌明的今天,它显然也不应该是一种完全依赖于经验积累的玄学。我们可以从计算机科学的基本原理--信息论的角度出发给出一些客观的评判标准。

## 一. 业务代码开发是否独立于框架特有的接口和对象
## 一. 业务开发能否独立于框架

近二十年以来,软件框架技术领域的一个最重要的进展就是认识到框架的中立性(framework agnostic)。业务代码开发本质上是对业务信息的一种表达,这种表达原则上应该独立于任何软件框架,甚至是独立于任何技术因素。**框架的作用是辅助我们用最自然、最直观的方式来表达业务信息,同时能够满足性能和其他技术规范要求****最理想的框架,应该是在开发业务代码时完全意识不到它存在的框架**
近二十年以来,软件框架技术领域的一个非常重要的进展就是认识到框架的中立性(framework agnostic)。业务代码开发本质上是对业务信息的一种表达,这种表达原则上应该独立于任何软件框架,甚至是独立于任何技术因素。**框架的作用是辅助我们用最自然、最直观的方式来表达业务信息,同时能够满足性能和其他技术规范要求****最理想的框架,应该是在开发业务代码时完全意识不到它存在的框架**

这样做的好处在于我们可以**避免业务域和纯技术域之间的信息渗透和污染**,使得业务代码的测试、技术框架的升级甚至更换变得异常简单。

### POJO和轻量级框架

轻量级框架的星星之火是从反对EJB这种重型容器技术开始燃起的。传统的框架往往关注重点是提供功能特性,在使用框架的过程中不可避免的在业务代码中会引用框架特有的对象、插入框架特有的函数调用等,这使得业务功能的实现与具体运行环境强绑定,甚至和框架的某个版本强绑定。而轻量级框架首次推广了POJO的概念,在编写一般性的业务代码时,业务代码所操纵的对象在形式上就是没有任何框架依赖的普通Java对象。比如Spring1.0的配置中,Bean的实现完全不需要具有Spring框架的知识,在beans.xml配置文件中即可实现任意复杂的bean的装配。
轻量级框架的星星之火是从反对EJB这种重型容器技术开始燃起的。传统的框架往往关注重点是提供功能特性,在使用框架的过程中不可避免的在业务代码中会引用框架特有的对象、插入框架特有的函数调用等,这往往使得业务功能的实现与具体运行环境强绑定,甚至和框架的某个版本强绑定。而轻量级框架首次推广了POJO的概念,在编写一般性的业务代码时,业务代码所操纵的对象在形式上就是没有任何框架依赖的普通Java对象。比如Spring1.0的配置中,Bean的实现完全不需要具有Spring框架的知识,在beans.xml配置文件中即可实现任意复杂的bean的装配。

在这一观点下,传统的SpringMVC框架的如下设计是不合适的,

```javascript
public void myMethod(HttpServletRequest request){
...
...
}

@RequestMapping("/test")
public ModelAndView test(){
ModelAndView mav=new ModelAndView("hello");
mav.addObject("time", new Date());
mav.getModel().put("name", "caoyc");

return mav;
}
```

在新的Web框架设计中,一般都是接收POJO对象,返回POJO对象,除了函数上的注解之外没有任何框架依赖的痕迹,而注解本身又只是一些纯粹的描述性信息

```javascript

@POST
@Path("/my-method2")
ApiResponse<MyResponseBean> myMethod2(MyRequestBean requestBean){
...
...
}

```

在Nop平台中NopGraphQL框架直接将请求参数规范化为一个JSON对象,而不需要像传统Web框架那样引入传参的N种方式(通过param传,通过restPath传,通过cookie传等)。这使得业务代码脱离了对Web运行环境的依赖,同样的服务函数可以直接注册为Kafka消息队列的响应函数,或者批处理文件的处理函数,进行自动化测试的时候也不需要Mock Web服务器,测试成本大为降低。例如,只要我们提供了在线的针对单个账户的入账服务,则无需编程,通过简单配置就可以得到一个每晚定时运行的读取批处理文件执行的批量入账服务
> 注解可以用最小化的、框架中立的方式引入所需信息。例如在Dao访问层,现在大量的ORM框架都可以识别JPA注解,这使得同一个实体定义可以应用于多种ORM框架
> 前端Redux和Vuex框架本质上也都是将action规范化为针对单个POJO对象的单参数函数
依赖于框架特有的接口仅仅是一个表象,本质上它导致的问题是通过接口依赖间接使得业务逻辑与某种特定的运行时技术环境产生耦合。比如,它导致在Controller中编写的代码只能用于Web请求处理,不能直接在二进制RPC层复用,也不能直接作为消息队列处理函数被复用

在Dao访问层,现在大量的ORM框架都可以识别JPA注解,这使得同一个实体定义可以应用于多种ORM框架。
在Nop平台中,NopGraphQL框架将请求参数规范化为一个JSON对象,而不需要像传统Web框架那样引入传参的N种方式(通过param传,通过restPath传,通过cookie传等)。这使得业务代码脱离了对Web运行环境的依赖,同样的服务函数可以直接注册为Kafka消息队列的响应函数,或者批处理文件的处理函数,进行自动化测试的时候也不需要Mock Web服务器,测试成本大为降低。例如,只要我们提供了在线的针对单个账户的入账服务,则无需编程,通过简单配置就可以得到一个每晚定时运行的读取批处理文件执行的批量入账服务。

> 前端Redux和Vuex框架本质上也都是将action规范化为针对单个POJO对象的单参数函数。
### 虚拟DOM和Hooks

Expand All @@ -77,18 +76,80 @@

Hooks概念的发展使得前端界面逻辑的表达可以摆脱组件对象的形式束缚,只需要引入最小化的Hooks假定,就可以将业务逻辑抽象到框架无关的纯逻辑函数中。借助于Hooks这样的抽象,Headless的组件库逐渐开始占据主流,并且可以用同一套核心代码适配React/Vue/Angular等多种基础框架。

> 传统上前端编程总是依附于某种组件框架,代码需要作为Component类的成员属性和成员函数,这导致代码与某种特定的组件语法、特定的组件运行时形成耦合。
## 二. 框架进行了哪些自动推导?

一个框架如果具有本质上的优越性,那么它一定是**相比于其他可选方案更充分的利用了某些信息,并且基于这些信息自动推导完成了大量的工作**


### 注解 vs. XML配置

这些年来XML配置日渐式微,取而代之的一般是注解技术。注解相对于XML配置最重要的优点是它**依附于程序语言的语法结构**,在强类型语言中还可以利用已经存在的类型信息,从而极大降低了需要表达的信息量。
如果使用XML配置,大量的工作其实是在搭建基本的对象结构,这不仅导致重复工作,同时还带来了XML配置结构和对象结构之间的同步问题。特别是当代码重构的时候,注解可以利用IDE已有的重构能力,而XML配置则往往游离于重构工具之外。

有趣的是,在重度模型驱动的低代码场景下,情况又有了新的变化。在手工编写代码的情况下,代码是最可靠的信息源,一切其他信息都是从代码信息衍生得到。而在模型驱动的场景下,模型是Unique Source of Truth,
有趣的是,在重度模型驱动的低代码场景下,情况又有了新的变化。在手工编写代码的情况下,代码是最可靠的信息源,一切其他信息都是从代码信息衍生得到。**在模型驱动的场景下,模型是Unique Source of Truth**
代码也只是根据模型衍生得到的一种信息表达形式。当需要重构的时候,我们只需要修改模型,自动就可以实现代码以及相关配置的修改。在这种情况下,就不存在XML重复表达信息,以及XML与代码之间信息需要同步的问题了。

XML相比于注解并不是一无是处,它有着自己独特的优势,在模型驱动的场景下这种优势得到放大,因此在Nop平台中我们的做法是以XML配置为基础,以注解为次要的补充形式。在后面我还会详细解释其中的原因。

### ORM中的自动推理

Hibernate出道即巅峰,它确立了所谓ORM框架这个品类的基本形态(后续的ORM框架一般只是Hibernate的简化版),这其中的价值就在于ORM隐含进行了如下推导:

1. 自动实现数据库记录和Java对象之间的相互映射,自动实现字段类型转换,自动生成实体主键。

2. 按照主键自动进行缓存。如果两次查询结果中包含同样的实体对象,则始终会返回同样的Java对象。这既提升了性能,又在某种意义上提升了应用的事务隔离级别:可以做到某种类似Repeatable Read的效果。

3. 通过dirty检查识别对POJO属性的修改,自动生成insert/update语句,无需手工调用dao.update(entity)操作,而且可以自动利用JDBC batch机制进行性能优化。

4. 借助Spring的声明式事务机制实现自动的事务提交和失败回滚。

5. 关联对象自动实现延迟加载

6. 自动利用外键关系推导出多表关联条件,比如where a.b.c = 3会自动利用a和b的关联条件以及b和c的关联条件自动生成a、b、c这三个表的关联条件。如果前台已经实现了针对单表的查询显示,则将字段名修改为a.b.c就自动可以实现多表联合查询。

NopOrm框架引入了更多的自动推理:

1. 自动将纵表转换为虚拟的横表:扩展字段可以保存在(entityName,entityId,fieldName,fieldValue)这样的纵表中,但是程序中使用时与数据库原生字段相同,同时可以通过SQL语法实现对扩展字段的查询和排序。

2. 自动跟踪实体属性修改,记录修改日志,并与自动化测试框架集成,提供数据库层面的录制回放。

3. 与NopGraphQL引擎结合,自动将领域模型发布为GraphQL服务。

### 自动推导的陷阱

之所以能够自动推导,肯定是因为引入了某些额外的假设。如果这些假设偏离了实际情况,那么就可能产生反效果。比如说,

1. Hibernate可以设置关联属性eager加载,但是并不是每次请求数据都必然需要加载关联数据,这可能导致不必要的性能损耗。NopOrm框架选择了所有关联属性都延迟加载,需要优化加载时再使用BatchLoadQueue机制。

2. Hibernate可以设置session的FlushMode为auto,由框架根据情况判断是否自动将修改刷新到数据库中。但这一机制的使用经常导致意料之外的数据库操作,很小的程序改动就可能导致应用性能大幅下降。NopOrm框架选择取消这种自动机制,只支持手动flush。

## 三. 框架提供了哪些自动转换?

这里的自动转换指的是信息的核心内容并没有发生变化,只是从一种表示形式转换为另一种表示形式。自动转换本质上是信息自动推导的一个特例,但是因为它的设计非常通用,所以值得单独强调一下。



1. Nop平台可以实现XML和JSON之间的自动转换。在Nop平台的前台我们引入了百度AMIS框架,并可以使用XML、JSON、YAML等多种文件格式编写AMIS页面代码。

2.

### 四. 在框架之外如何使用相关信息?

### 独立存在的模型信息

### 独立诊断和调试

### 信息管道

### 五. 框架的设计完整性如何?

### 分层设计

### 异步处理

## 六. 框架提供了哪些差量化机制?

### 七. 整体代码量是否更小?

# 总结

0 comments on commit 0d6f787

Please sign in to comment.