这两个概念其实没什么神秘的,固然此文章中的这两个概念以曾老师的课程为准(关于CQRS和DDD的标准概念,google上已经不少了,再也不赘述。)ios
DDD(Domain Driven Design),领域驱动设计开发。
DDD和OOP有什么同吗?其实就我我的经验来讲,没有任何不一样(固然你能够反驳我),DDD就是OOP。这里以曾老师课上的概念为准,domain就是世界,包含了当前全部actor的一个域,这个域是一个上帝视角,能够监听每个域中发生的事件,而且记录。git
CQRS,既命令和查询职责分离(Command Query Responsibility Segregation)。
在普通mvc架构中,对于数据库的CRUD基本都是写在controller层,这样一来路由很是臃肿,并且维护起来简直是噩梦。github
CQRS将查询与职责分离。简单说来,就是写操做和读操做分离,读操做写在路由中,写操做经过面向对象写入类的业务方法中,这样路由中的查询部分薄了,并且对于写操做的可读性,重用性和维护性大大提升。数据库
相比较于普通mvc,cqrs分为核心层Core(及核心层扩展Core Extension)应用层(Application),UI层,看起来3层,实际上是四层,可是因为核心层与核心层扩展的伸缩性很强,而且针对项目的大小来决定,因此就我以为用3.5层来描述比较合适。express
取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实例对象的操做,关注与取关的操做。json
Any fool can write code that a computer can understand. Good programmers write code that humans can understand. -- 某位大牛
以上的例子很好的诠释了可读性还有重用性。对于写操做来讲,彻底用业务方法来实现,那么路由中能够仅包含cqrs中Q的部分,这样作到了业务和查询分离,那么迷惑也开始解开了。axios
在使用普通mvc的时候,逻辑和查询一般都会放在路由当中,这样形成的高耦合性(coupling)让代码的重用性,可读性,可伸缩性不好。维护起来简直噩梦连连。个人第一个项目是用标准mvc完成,后期加新需求的时候基本山就是牵一发动全身,也是个人经验确实不够对于不少地方没有对代码进行可重用的封装。segmentfault
Auxo框架集成了Nuxt(Vue),Vuex,Express,cqrs四个重要框架。这样在开发时就不用再辛苦搭建开发环境了,直截了当。Auxo是约定式的框架,关于文件结构是根据Nuxt(传送门)的,因此有必要读一读Nuxt的文档,对Nuxt有必定了解以后就能够用了并且上手很快,由于基本上不须要配置什么东西。架构
在Auxo框架中,数据遵循Event Sourcing原则,分两个collection。
记录事件对象的数据库,能够经过该数据库的数据进行数据回溯。
事件快照。domain中的事件的一个snapshot,我暂且理解为一个log
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对象的方法进行数据操做。
axios
和 domain
这两个对象已经写在plugins/
文件夹里面,能够直接在Vue组件中引用以下
<!-- Vue Template --> </template> <script> import axios from '@/plugins/aixos' import domain from '@/plugins/domain' // ... codes ... </script> <style> /* some style sheet */ </style>
起初看到曾老师用listener可是不明白怎么监听,并且去看epxress-cqrs的源码的时候,看到listener的路径是做参数与传入了的。
截取一段express-cqrs的源码
// Register Actors Class from actors folder ActorList.filter(Actor => /.*\.js$/.test(Actor)). forEach(Actor => domain.register(require(path.join(actorPath, Actor)))); // Get Listener from listener folder listeners.filter(listener => /.*\.js$/.test(listener)). forEach(listener => require(path.join(listenerPath, listener))(domain));
Listener 内部写法,以下(我的经验)
module.exports = function(domain){ // Utilise domain.on(...) to make onAction listening }
此次先暂时聊这么多,cqrs还有不少好用的方法和思想能够慢慢琢磨,并且这种编程思想易实践,而且对全局的把控更精准,心有猛虎细嗅蔷薇,固然这篇文章也是针对上过曾老师课的童鞋们,不算是扫盲,事后会继续写一些关于cqrs框架应用的文章,也欢迎你们提问,而且一块儿讨论。若是有错误,也请你们指正,我会立刻修改。
来源:https://segmentfault.com/a/1190000016772949