Skip to content

Latest commit

 

History

58 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

项目简介

这个是 DCE-UI 的数据层。整体使用了 RxJS 5 作为主要架构。目前的形式是通过 restify 框架暴露 API 接口来使用。但是由于本项目依赖很轻量,而且核心的数据层和 API 的耦合很低,所以也可以很方便地迁移到前端使用,并且理论上可以兼容所有主流前端框架。

启动方法

npm start:在本地的 4000 端口启动服务器,具体路由参看 index.js 里的内容。

npm run build:用 babel 编译代码,结果在 dist 目录中。

npm run webpack:用 wenpack 打包代码,结果在 build 目录中。

npm run watch:持续用 babel 编译源代码。

项目详细介绍

目录结构

├── app 应用目录

│ ├── api 数据的 API 接口

│ ├── constant 一些常量

│ ├── factory 数据的工厂函数

│ ├── stream 数据流

│ ├── util 一些方便的函数

│ ├── index.js 应用入口文件

│ └── index.js 用来编译浏览器版代码,可以无视

├── build

│ └── bundle.js webpack 打包结果

├── dist babel 编译后的结果

├── gulpfile.js

├── package.json

├── index.html 用来测试编译出来的浏览器版代码,可以无视

└── webpack.config.js webpack 配置文件

架构概述

应用入口

整个应用的入口在 app/index.js,因为 restify 服务器是在这个文件中启动的。但事实上它不是整个数据层的入口,因为 restify 只是暴露数据层的一种形式,未来也可以直接把数据层移植到前端。

数据层的入口

整个数据层的入口初始化在 stream/hub.js 中。初始化流程如下。

  1. 先调用一些基本的 API,比如 apiInfo,然后保存在 α$$ 中。α$$ 是整个应用最顶层的 Subject。
  2. 当 α$$ 发布数据时,代表应用基本的 API 调用完毕。可以开始启动 socket 了。
  3. socket 的事件推送会被包装成 hub$$。hub$$ 是整个应用次顶层的 Subject,它收集所有 socket 事件以及自定义事件,并且把对应的事件分发给对应的数据流。
  4. 最后 hub$$ 会分离出很多 Observable,比如 app$、service$,由此产生各类数据的数据流。
  5. 最开始 hub$$ 会默认发布一个 init 事件,来获取所有数据。

数据流

从 hub$$ 衍生出来的数据流也有各自的逻辑。hub$$ 的作用仅仅是通知数据流去更新,但不会告诉数据流怎么更新。数据流的逻辑一般如下:

  1. 接收到 hub$$ 的更新通知。
  2. 调用 API 发送请求。
  3. merge 或者 combineLatest 其他数据流中的数据,然后把数据通过工场函数格式化。
  4. 格式化好的数据就是最后要暴露出来的结果,保存在 BehaviorSubject 中,比如 appsVm$$。视图层只要直接订阅这个 BehaviorSubject 就可以了。

工厂函数

整个数据层最关键的部分就是数据格式化的部分,它有这么几个难点:

  1. 格式化一个数据往往需要多个数据。
  2. 格式化的时候往往要发请求获取数据。
  3. 当数据更新时,难以复用原来的格式化逻辑。

这些难点统统可以通过 RxJs 解决,请看它是如何解决这些问题的:

  1. 通过 merge、zip、combineLatest 等 operator 来合成数据。
  2. 在一开始获取所有数据,形成数据池。这样就能最大程度地重用数据,避免大部分请求。即使要发,由于 RxJs 强大的异步控制能力,也并不是难事。
  3. 整个 RxJs 数据流形成了一个闭环,数据流是单一、可预测的。从始至终,每种数据都只有一套逻辑。

为了确保这些功能,工场函数必须要是纯函数:

  1. 工厂函数使用 class。虽然名字不同,但结果是一样的。
  2. class 中的所有方法必须是纯函数。即使有地方不纯,也要注释标明。
  3. 如果上面一点做不到,那至少要保证整个 class 是纯的。如果格式化过程中需要发请求,就在数据传入 class 之前发好,然后把结果和元数据一起作为参数传入 class 中。
  4. 如果 class 是纯的,那么即使数据出了问题,也很容易定位。如果 class 不纯,那么逻辑就乱了,出了问题很难追踪。

命名约定

  • 以 $ 结尾代表这个变量是 Observable。
  • 以 $$ 结尾代表这个变量是 Subject,或者 BehaviorSubject。
  • 以 _ 结尾代表这个变量时 Subscription。

添加新数据流的流程

由于架构有点复杂,所以把流程写下来,以后这部分应该可以自动化。下面以添加 app 的数据流为例。

  1. 在 api 目录中,新建 app.js 文件,在里面添加好 app 的 api。
  2. 在 hub 中分出一个 app$,并且记得要在 init 的时候初始化它。
  3. 在 factory 目录中,新建 app.js 文件,暴露出一个 AppClass。
  4. 在 stream 目录中,新建 app.stream 文件,引用 app 的 api、AppClass、app$,以及其他必要的数据,然后定义好数据流,最终暴露出 appVm$$。

常见问题

  1. 现在 hub$$ 完全依赖 socket 更新数据,未来要手动更新数据怎么办呢?

    未来如果要手动更新数据应该会采用类似 redux 那样的形式,并且把它封装成一个 Observable,merge 进 hub$$ 中。这样整个应用就能形成一个闭环。

待办事项

  1. 单元测试
  2. 进一步完善注释

维护者

Bowen

About

No description, website, or topics provided.

Resources

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages