CQRS是啥?DDD又是啥?

这两个概念其实没什么神秘的,当然此文章中的这两个概念以曾老师的课程为准(关于CQRS和DDD的标准概念,google上已经很多了,不再赘述。)

DDD(Domain Driven Design),领域驱动设计开发。

DDD和OOP有什么同吗?其实就我个人经验来说,没有任何不同(当然你可以反驳我),DDD就是OOP。这里以曾老师课上的概念为准,domain就是世界,包含了当前所有actor的一个域,这个域是一个上帝视角,可以监听每一个域中发生的事件,并且记录。

CQRS,既命令和查询职责分离(Command Query Responsibility Segregation)。

在普通mvc架构中,对于数据库的CRUD基本都是写在controller层,这样一来路由非常臃肿,而且维护起来简直是噩梦。

CQRS将查询与职责分离。简单说来,就是写操作和读操作分离,读操作写在路由中,写操作通过面向对象写入类的业务方法中,这样路由中的查询部分薄了,而且对于写操作的可读性,重用性和维护性大大提高。

相比较于普通mvc,cqrs分为核心层Core(及核心层扩展Core Extension)应用层(Application),UI层,看起来3层,其实是四层,但是由于核心层与核心层扩展的伸缩性很强,并且针对项目的大小来决定,所以就我觉得用3.5层来描述比较合适。

取cqrs文档中的例子


const {Actor} = require("cqrs"); module.exports = class User extends Actor{
constructor(data){
const {name} = data;
super({
name,
createTime: Date.now(),
stars:[], // 被关注明星的 ids
watchers:[] // 关注者的 ids
});
} // 关注某位明星
async follow(starId){
const service = this.service;
const star = await service.get("User",starId);
if(starId !== this.id && star){
await star.addWatcher(this.id);
this.$(starId)
}
} // 取消关注某位明星
async unFollow(starId){
const star = await this.service.get("User",starId);
if(star){
await star.deleteWatcher(this.id);
this.$(starId);
}
} // 加入关注者 watcher
addWatcher(watcherId){
if(watcherId !== this.id)
this.$(watcherId);
} // 取消被关
deleteWatcher(watcherId){
this.$(watcherId);
} get updater(){
return {
follow(json, event){
const stars = json.stars;
stars.push(event.data);
return {
stars
}
},
unFollow(json, event){
const stars = json.stars; var set = new Set(stars);
set.delete(event.data); return {
stars:[...set]
}
},
addWatcher(json,event){
const watchers = json.watchers;
watchers.push(event.data);
return {
watchers
}
},
deleteWatcher(json,event){
const watchers = json.watchers;
const set = new Set(watchers);
set.delete(event.data);
return {
watchers:[...set]
}
}
}
}
}

以上例子是一个cqrs (传送门)Actor的实现,通过this.$产生一个事件,事件由updater接收,进行数据的真正修改。


const {Domain} = require("cqrs");
const User = require("./User");
const domain = new Domain(); // 注册 User Actor 类
domain.register(User); // 即时异步执行函数
(async function () { // 创建用户1
let user1 = await domain.create("User",{
name:"leo"
}); // 创建用户2
let user2 = await domain.create("User",{
name:"zengliang"
}) // user1 关注 user2
await user1.follow(user2.id); console.log(user1.json.stars); // 打印一下 user1 监听所有 ids
console.log(user2.json.watchers); // 打印一下 user2 追随者的所有 ids user1.unFollow(user2.id); // user1 取消关注 user2 // 重新加载 user1 和 user2
user1 = await domain.get("User",user1.id);
user2 = await domain.get("User",user2.id); console.log(user1.json.stars); // 打印一下 user1 监听所有 ids
console.log(user2.json.watchers); // 打印一下 user2 追随者的所有 ids })();

以上是在运行中对User实例对象的操作,关注与取关的操作。

Any fool can write code that a computer can understand. Good programmers write code that humans can understand. -- 某位大牛

以上的例子很好的诠释了可读性还有重用性。对于写操作来说,完全用业务方法来实现,那么路由中可以仅包含cqrs中Q的部分,这样做到了业务和查询分离,那么迷惑也开始解开了。

  • 写操作,用业务方法来完成,属于核心层
  • query,既查询操作,写在router中,是应用层变薄

在使用普通mvc的时候,逻辑和查询通常都会放在路由当中,这样造成的高耦合性(coupling)让代码的重用性,可读性,可伸缩性很差。维护起来简直噩梦连连。我的第一个项目是用标准mvc完成,后期加新需求的时候基本山就是牵一发动全身,也是我的经验确实不够对于很多地方没有对代码进行可重用的封装。

现在浅谈一下 Auxo(传送门)

Auxo框架集成了Nuxt(Vue),Vuex,Express,cqrs四个重要框架。这样在开发时就不用再辛苦搭建开发环境了,直截了当。Auxo是约定式的框架,关于文件结构是根据Nuxt(传送门)的,所以有必要读一读Nuxt的文档,对Nuxt有一定了解之后就可以用了而且上手很快,因为基本上不需要配置什么东西。

在Auxo框架中,数据遵循Event Sourcing原则,分两个collection。

  • 一个是事件数据库记录在domain中发生的所有事件,让事件回溯、长故事(saga)和事件锁(lock)成为可能;
  • 另外一个是查询数据库,记录普通数据,我自己的理解就是面向数据库开发的那种最基本的数据库。

eventstore

记录事件对象的数据库,可以通过该数据库的数据进行数据回溯。

snap

事件快照。domain中的事件的一个snapshot,我暂且理解为一个log

server/index.js 中的 req.dbs req.$domain

这两个属性已经在框架中直接挂载在了req对象上,归功于曾老师。在server/index.js中,已经定义好了,这个文件相当于express的app.js,只是文件名不一样。
req.dbs就是上述的查询数据库,可以使用mongojs来query。
req.$domain就是domain,即上帝视角,可以用以下语句


req.$domain.get('User', uid); // 获取User对象
req.$domain.create('User', {username: 'ephraimguo', password:'*******'}); // 创建user对象

等domain对象的方法进行数据操作。

在Vue组件中的axiosdomain

这两个对象已经写在plugins/文件夹里面,可以直接在Vue组件中引用如下

```<template>
<!-- Vue Template -->
</template>
<script>
import axios from '@/plugins/aixos'
import domain from '@/plugins/domain'
// ... codes ...
</script>
<style>
/* some style sheet */
</style>
```

Listener 核心层扩展 (有个小坑)

起初看到曾老师用listener但是不明白怎么监听,而且去看epxress-cqrs的源码的时候,看到listener的路径是作参数与传入了的。

截取一段express-cqrs的源码


// Register Actors Class from actors folder
ActorList.filter(Actor =&gt; /.*\.js$/.test(Actor)).
forEach(Actor =&gt; domain.register(require(path.join(actorPath, Actor)))); // Get Listener from listener folder
listeners.filter(listener =&gt; /.*\.js$/.test(listener)).
forEach(listener =&gt; require(path.join(listenerPath, listener))(domain));
  • 第一步,在根目录下添加listener文件夹
  • 第二部,创建新的监听js文件,

Listener 内部写法,如下(个人经验)



module.exports = function(domain){
// Utilise domain.on(...) to make onAction listening
}

这次先暂时聊这么多,cqrs还有很多好用的方法和思想可以慢慢琢磨,而且这种编程思想易实践,并且对全局的把控更精准,心有猛虎细嗅蔷薇,当然这篇文章也是针对上过曾老师课的童鞋们,不算是扫盲,过后会继续写一些关于cqrs框架应用的文章,也欢迎大家提问,并且一起讨论。如果有错误,也请大家指正,我会马上修改。

来源:https://segmentfault.com/a/1190000016772949

CQRS框架(nodejs的DDD开发落地框架)初识感想的更多相关文章

  1. DDD实战进阶第一波(三):开发一般业务的大健康行业直销系统(搭建支持DDD的轻量级框架二)

    了解了DDD的好处与基本的核心组件后,我们先不急着进入支持DDD思想的轻量级框架开发,也不急于直销系统需求分析和具体代码实现,我们还少一块, 那就是经典DDD的架构,只有了解了经典DDD的架构,你才能 ...

  2. DDD实战进阶第一波(四):开发一般业务的大健康行业直销系统(搭建支持DDD的轻量级框架三)

    上一篇文章我们讲了经典DDD架构对比传统三层架构的优势,以及经典DDD架构每一层的职责后,本篇文章将介绍基础结构层中支持DDD的轻量级框架的主要代码. 这里需要说明的是,DDD轻量级框架能够体现DDD ...

  3. 转-基于NodeJS的14款Web框架

    基于NodeJS的14款Web框架 2014-10-16 23:28 作者: NodeJSNet 来源: 本站 浏览: 1,399 次阅读 我要评论暂无评论 字号: 大 中 小 摘要: 在几年的时间里 ...

  4. 应用程序框架实战十七:DDD分层架构之值对象(层超类型篇)

    上一篇介绍了值对象的基本概念,得到了一些朋友的支持,另外也有一些朋友提出了不同意见.这其实是很自然的事情,设计本来就充满了各种可能性,没有绝对正确的做法,只有更好的实践.但是设计与实践的好与坏,对于不 ...

  5. 应用程序框架实战十三:DDD分层架构之我见

    前面介绍了应用程序框架的一个重要组成部分——公共操作类,并提供了一个数据类型转换公共操作类作为示例进行演示.下面准备介绍应用程序框架的另一个重要组成部分,即体系架构支持.你不一定要使用DDD这样的架构 ...

  6. 应用程序框架实战十三:DDD分层架构之我见(转)

    前面介绍了应用程序框架的一个重要组成部分——公共操作类,并提供了一个数据类型转换公共操作类作为示例进行演示.下面准备介绍应用程序框架的另一个重要组成部分,即体系架构支持.你不一定要使用DDD这样的架构 ...

  7. SOD框架的数据容器,打造最适合DDD的ORM框架

    SOD框架的数据容器,打造最适合DDD的ORM框架 引言:DDD的困惑 最近,我看到园子里面有位朋友的一篇博客 <领域驱动设计系列(一):为何要领域驱动设计? >文章中有下面一段话,对DD ...

  8. java web后台开发SSM框架(Spring+SpringMVC+MyBaitis)搭建与优化

    一.ssm框架搭建 1.1创建项目 新建项目后规划好各层的包. 1.2导入包 搭建SSM框架所需包百度云链接:http://pan.baidu.com/s/1cvKjL0 1.3整合spring与my ...

  9. 渐进式框架、自底向上增量开发的vue

    官网对vue.js的介绍是这样的: 一套构建用户界面的 渐进式框架.与其他重量级框架不同的是,Vue 采用自底向上增量开发的设计. 那么到底什么是渐进式框架.自底向上增量开发又是什么呢? 其实我觉得这 ...

随机推荐

  1. Q15格式表示负小数

    1.用Q15.16-bit格式,表示出-0.5? 解析:其实很简单,Q15是dsp里为了优化浮点的,就是将小数* 2^15. 例如:0.333 * 32768 = 10911.744  取整数就是10 ...

  2. BZOJ 4011: [HNOI2015]落忆枫音 计数 + 拓扑排序

    Description 「恒逸,你相信灵魂的存在吗?」 郭恒逸和姚枫茜漫步在枫音乡的街道上.望着漫天飞舞的红枫,枫茜突然问出 这样一个问题.  「相信吧.不然我们是什么,一团肉吗?要不是有灵魂……我们 ...

  3. 利用word宏功能一键导出数据库表结构

    前言: 需求是: 为了完成<数据库设计文档>中的表结构展示,需要导出所有的表结构,包括字段名.长度.注释等必要标题. 数据库:MySQL 我选择的方法是——用word的宏功能导出.很多博客 ...

  4. [THUSC2017]杜老师:bitset+线性基

    算法一(50pts) 分析 有一个很显然的暴力做法,对于区间内的每个数开个bitset,然后暴力分解质因数.如果对于一个数,它的一个质因子的指数是奇数,那么就把bitset的对应位设成\(1\).答案 ...

  5. POST上传多张图片配合Django接受多张图片

    POST上传多张图片配合Django接受多张图片 本地:POST发送文件,使用的是files参数,将本地的图片以二进制的方式发送给服务器. 在这里 files=[("img",op ...

  6. 后盾网lavarel视频项目---phpstorm 配置ftp, 自动更新同步代码

    后盾网lavarel视频项目---phpstorm 配置ftp, 自动更新同步代码 一.总结 一句话总结: 1.在phpstorm中设置:路径Tools/Deployment/Configuratio ...

  7. JavaScript基础篇详解

    全部的数据类型: 基本数据类型: undefined Number Boolean null String 复杂数据类型: object ①Undefined: >>>声明但未初始化 ...

  8. FireMonkey 绘图(1)

    FireMonkey 绘图(1) FMX 的 Canvas 在不同的系统上会分别使用:WinVista.Win7: D2D (FMX.Canvas.D2D.pas)WinXP: GDI+ (FMX.C ...

  9. Nginx1.8源码包编译安装

    1.下载解压Nginx,为方便管理下载包一般将下载包放在指定目录下 ,即/usr/local/src/下. wget http://nginx.org/download/nginx-1.8.0.tar ...

  10. 测开之路一百五十五:jquery-validation前台数据验证

    前面做的wtform验证是服务器端的验证,需要把数据传输到服务器,服务器验证后再吧结果传输到前端,网络慢的时候,用户体验不好,所以需要前端验证,且后端验证不能少 传统的js或者jquery如果要验证信 ...