什么是CQRS?什么是DDD?这两个概念没有什么神秘的。当然,本文中的两个概念是基于曾老师的课程(CQRS和DDD的标准概念Google上已经有很多,不再赘述。)DDD(DomainDrivenDesign),领域驱动设计和发展。DDD和OOP有什么区别?其实就我个人的经验来说,没有什么区别(当然你可以反驳我),DDD就是OOP。这里以曾老师课堂上的概念为准。领域是世界,一个包括所有当前参与者的领域。这个域是一个上帝视角,可以监控和记录各个域中发生的事件。CQRS,既有命令查询责任分离(CommandQueryResponsibilitySegregation)。在普通的MVC架构中,对于数据库的CRUD基本都写在controller层,所以路由非常臃肿,维护起来简直就是噩梦。CQRS将查询与职责分开。简单来说就是写操作和读操作分离。通过面向对象,读操作写在路由中,写操作写在类的业务方法中。这样路由中的查询部分变细了,提高了写操作的可读性。可重用性和可维护性大大提高。与普通mvc相比,cqrs分为核心层Core(和核心层扩展CoreExtension)应用层(Application)、UI层,看似三层,实际上是四层,但由于核心层的可扩展性和corelayerextension很强,而且是根据项目的大小来定的,所以我觉得用3.5层来形容比较合适。以cqrs文档中的例子为例const{Actor}=require("cqrs");module.exports=classUserextendsActor{constructor(data){const{name}=data;super({name,createTime:Date.now(),stars:[],//被关注明星的idwatchers:[]//被关注者的id});}//关注一个明星asyncfollow(starId){constservice=this.service;conststar=awaitservice.get("用户",starId);if(starId!==this.id&&star){awaitstar.addWatcher(this.id);this.$(starId)}}//取消关注明星asyncunFollow(starId){conststar=awaitthis.service.get("User",starId);if(star){等待star.deleteWatcher(this.id);这个.$(starId);}}//加入FollowerwatcheraddWatcher(watcherId){if(watcherId!==this.id)this.$(watcherId);}//取消关闭deleteWatcher(watcherId){this.$(watcherId);}getupdater(){return{follow(json,event){conststars=json.星星;星星。推(事件。数据);返回{stars}},unFollow(json,event){conststars=json.stars;varset=newSet(星星);设置。删除(事件。数据);返回{stars:[...set]}},addWatcher(json,event){constwatchers=json.watchers;watchers.push(事件.data);返回{watchers}},deleteWatcher(json,event){constwatchers=json.watchers;constset=newSet(watchers);设置。删除(事件。数据);return{watchers:[...set]}}}}}上面的例子是一个cqrs(portal)Actor的实现,通过this.$产生一个事件,事件被updater接收,进行实际修改数据const{Domain}=require("cqrs");constUser=require("./User");constdomain=newDomain();//RegisterUserActorclassdomain.register(User);//立即异步执行Function(asyncfunction(){//创建用户1letuser1=awaitdomain.create("User",{name:"leo"});//创建用户2letuser2=awaitdomain.create("User",{name:"zengliang"})//user1关注user2awaituser1.follow(user2.id);console.log(user1.json.stars);//打印user1监听所有idconsole.log(user2.json.watchers);//打印user2的所有关注者user1.unFollow(user2.id);//user1取消关注user2//重新加载user1和user2user1=awaitdomain.get("User",user1.id);user2=awaitdomain.get("User",user2.id);console.log(user1.json.stars);//打印user1监控所有idsconsole.log(user2.json.watchers);//打印查看所有user2的关注者的id})();以上就是运行时对User实例对象的操作,跟随和取消的操作。任何傻瓜都可以编写计算机可以理解的代码。优秀的程序员编写人类可以理解的代码。--上面大牛的例子就是一个很好的可读性和复用性的例子。对于写操作,完全由业务方法实现,所以路由只能包含cqrs的Q部分,这样业务和查询就分开了,混乱开始解决。写操作是用业务方法完成的,属于核心层查询,也就是说查询操作写在router里面,是应用层的细化。使用普通的mvc时,逻辑和查询通常都放在路由中,从而产生高耦合耦合使代码具有可重用性、可读性和可扩展性。这是维护的噩梦。我的第一个项目是用标准的mvc完成的。后期增加新要求时,基本山是调动全身。也是因为我的经验实在是不够,很多地方都没有对代码进行复用包装。下面说说Auxo(传送门)Auxo框架集成了Nuxt(Vue)、Vuex、Express、cqrs四大重要框架。这样在开发的时候就不用费心去搭建开发环境了,直截了当。Auxo是一个传统的框架。文件结构是基于Nuxt(传送门)的,所以有必要阅读Nuxt的文档。当你对Nuxt有了一定的了解之后,你就可以使用它,快速上手,因为基本上不需要任何配置。什么。在Auxo框架中,数据遵循EventSourcing原则,分为两个集合。一是事件数据库,记录域内发生的所有事件,使事件回溯、长篇故事(saga)和事件锁(lock)成为可能;另一种是查询数据库,记录常用数据。我自己的理解是对于数据库开发最基本的一种数据库。eventstore是一个记录事件对象的数据库,通过数据库中的数据可以追溯数据。快照事件快照。domain中的事件快照,暂时了解到logserver/index.js中的req.dbs和req.$domain这两个属性已经直接挂载到framework中的req对象上了,感谢曾老师。在server/index.js中,已经定义好了。这个文件相当于express的app.js,只是文件名不同。req.dbs就是上面的查询数据库,可以用mongojs查询。req.$domain是域,也就是上帝视角,可以使用如下语句req.$domain.get('User',uid);//获取用户对象req.$domain.create('User',{username:'ephraimguo',password:'*******'});//创建域对象的方法,例如用于数据操作的用户对象。Vue组件中的axios和domain这两个对象已经写在了plugins/文件夹下,可以在Vue组件中直接引用如下Listener核心层扩展(有个小坑)一开始看到曾老师用的是listener但是没看懂怎么监听,后来去看epxress-cqrs源码的时候看到listener的路径作为参数传入。拦截express-cqrs的源代码//从actors文件夹中注册Actors类ActorList.filter(Actor=>/.*\.js$/.test(Actor)).forEach(Actor=>domain.register(require(path.加入(演员路径,演员))));//从监听器文件夹获取监听器listeners.filter(listener=>/.*\.js$/.test(listener)).forEach(listener=>require(path.join(listenerPath,listener))(domain));第一步是在根目录下添加listener文件夹的第二部分,新建监听js文件。Listener内部的写法如下(个人经验)module.exports=function(domain){//利用domain.on(...)让onAction监听}这次就说这么多吧,cqrs还有很多有用的方法和思路可以慢慢琢磨,而且这种编程思路容易实践,对整体影响很大。当然本文也是针对上过曾老师课的童鞋们。讨论。如有错误请指正,我会第一时间修改。
