对于编辑器,或典型的富状态的复杂应用程序,状态管理相关的基础数据框架是最核心最基础的能力。这个基础至关重要,从我的实践观察来说可以断言,这部分内容如果没有做好,整个项目长期而言技术上一定失败。状态管理可能在不同人的视角下有不同指代。这里我们将其具体职责定义为:
- 用户定义数据的结构
- 用户增删查改数据
- 用户观察捕获数据变更并作出响应
如果状态管理不做任何框架级别的解决方案。采用手写实现,就相当于:
- 用户定义一些数据类型来表达业务逻辑
- 用户在这些类型上实现各自封装继承访问方法,和具体业务方法。并实际使用来完成业务需求
- 用户采用一些观察者模式的发布订阅组件,或者事件,来做一些数据变更后的自动响应。
对于小型应用,这么做并没有什么问题,但是对于数据结构,类型复杂,数据量大的大型应用,长期开发的维护成本高,上述的编写内容的本身的开发工作量非常高非常重复,并且性能很可能出现问题。因此,状态管理相关的数据框架是必需的。
对于web前端项目,类似的解决方案是多样的,有些还会直接融合视图层的功能。这些框架的核心除了基于不可变数据结构或者持久化数据结构的方案,大概率都要依赖javascript语言的动态能力,而追求极致性能和跨平台的方案必然采用类似类c的语言编写。那么在没有一个强大的runtime的帮助下,什么样的solution是足够好的,就是一个值得研究的问题。
从上述对这个职责的定义来说,不难看出我对这个问题的最佳解法,给出的答案,就是relational database。我认为应该通过实时的关系性数据库来建模维护富应用大型客户端程序的业务数据。
这个结论主要来自于工程上的考量:首先是normalization。
关系模型很简单,但是能够cover绝大多数的数据结构。可以认为所有的数据结构都可以normalize到关系模型上。这样的normalization是非常强大的。
假设你的数据都是存储在这样数据库中,那么
- 采用db提供的统一查询方式来增删查改
- 无需手工编写无数的数据的增删查改方法
- 数据的查询修改可以在db层面被统一的优化
- 采用db统一的方式做数据验证
- 在任何层面保证数据的正确性,无需手工编写和调用无数的检查方法
- 采用db统一的方式定义数据结构
- 直接其他通用数据建模语言定义的外部数据源进行数据交换,而不需要编写任何loader和exporter
- 自动的生成其他语言或者格式的接口实现,比如c FFI,protobuffer
- 可以自动的实现版本的数据兼容机制
- 自动的,不依赖编程语言的,实现所有数据的精确变更观测
- 自动的为所有数据实现增量的undo redo
- 自动的为所有数据实现增量的序列化反序列化和外部存储的持久化(增量保存)
- 作为响应式增量计算框架的数据源
- 实现版本控制,协同编辑等高级功能
脱离了这样的数据框架,甚至可以说编写大型应用在工程上是不现实的。统一的数据模型,能够编写针对任何类型的通用实现,而不用针对每一种具体类型编写实现
另一点在于,这种方式具备理论上最大的性能优势。
这样的性能优势一方面如同上文「数据的查询修改可以在db层面被统一的优化」所述,是工程上的,方法论上的。比如程序性能优化的问题,转变为dba数据库调优的问题,转变为通用的优化问题。
另一方面,这种数据建模方式,最为贴合面向数据的性能优化。比如默认所有的数据都是按列存储的(SOA),计算和查询都是批量化的,在连续的内存上访问和计算,其上直接对接data parallelization的框架。
关于面向数据的性能优化,另一个设计上的优势是,这种建模方式其区分了数据模型的定义,和数据存储方式的实现。比如用户可以关联几个field在内存中指定采用AOS来存储,比如用户可以指定某个field是稀疏的,需要特别的存储方式来做优化。