cqrs领域驱动设计nodejs框架总结
最近在熟悉曾老师的cqrs框架 ,了解了基本api,特总结一下。
cqrs (Command Query Responsibility Segregation),即命令查询的责任分离,该思想最早在 JDON 上被提出,该模式从业务上分离修改 (Command,增,删,改,会对系统状态进行修改)和查询(Query,查,不会对系统状态进行修改)的行为。从而使得逻辑更加清晰,便于对不同部分进行针对性的优化。
随着前端全栈的不断发展,涌现出了许多优秀的框架 koa,express等 MVC类的架构,但是随着系统的复杂度提升有必要进行重新思考。
DDD cqrs 框架 基于此类考虑实现了 node 端的领域驱动设计框架,总结下来有一下几个特点:
1、以客户需求为中心,建立对象不局限于getter,setter 贫血对象,要求建立富对象,因为客户是不管你的代码逻辑和实现方式的,关心的只是结果。举个栗子(伪代码):
class User extends Actor { constructor(data) { super({ enable: true, loginname: data.loginname, password: data.password, num: 0, nickname: data.loginname, email: data.email }) } static createBefor(data, service) { return new Promise((resolve, reject) => { service.get('UserRecorder', 'recorderid').then((record) => { resolve(); }) }) } enable(data, service) { if (!this.json.enable) { service.apply('enable') } } disable(data, service) { if (this.json.enable) { service.apply('disable') } } plus(data, service) { service.apply('plus', data.num); } * update(data, service) { let error; let recorder = yield service.get('UserRecorder', 'recorderid'); if (data.nickname) { } if (data.email) { } if (error) throw error; } updatePwd(data, service) { service.apply('updatePwd', data.password); } when(event) { super.when(event); switch (event.name) { case "updatePwd": this._data.password = event.data; break; case "updateEmail": this._data.email = event.data; break; case "updateNickName": this._data.nickname = event.data; break; case "plus": this._data.num += event.data; break; case "enable": this._data.enable = true; break; case "disable": this._data.enable = false; break; } } } module.exports = User;
2、职责分离。领域驱动的设计思想,在MVC的架构上,多了核心层(core),核心层只处理对象相关事务,底层实现,业务层交给上层(路由层),业务层的主要功能在于权限分配和主要业务逻辑。
3、事件驱动,cqrs的设计思想是事件驱动,事件驱动目的在于解耦,以前我们在处理两个或者三个对象之间的关系时,可能会这样写:
adminFunc.delNotifiesById(req, res, nidsArr[i], () => { adminFunc.getAdminNotices(req, res, function (noticeObj) { req.session.adminNotices = noticeObj; res.end('success'); }); });
该功能是删除提示消息,设置成功后再发消息给管理员。理论上没什么问题,一个回调函数解决问题。但是这里涉及到了对两个对象的操作(1、Notice 2、Message)
在cqrs思想中,我们可以在Message对象中对Notice的删除操作进行监听,当收到事件时,主动处理相应的逻辑。这一点跟redux的设计思想有点类似。
let eventname = Domain.Alias().actorType('Topic').name('create').get() domain.on(eventname, (event) => { domain.get('Topic', event.actorId).then((topicJson) => { domain.call(`User.${topicJson.authorId}.plus`, { num: 3 }); }) })
通过监听Topic的创建事件来对User对象做相应的操作。
4、cqrs框架具备事件回溯的功能,什么情况下需要事件回溯?你可能第一时间想到的是银行系统,或者灾备系统。用户的操作在某些情况下是需要记录的,而不是整个系统只用数据库记录操作结果。
总结一下cqrs的api:
1、创建对象的形式,参照上面的User对象
2、cqrs框架有一个容纳所有对象(actor)的容器 domain,domain的方法:
对象创建:
domain.create("User", { loginname, email, password }).then((json) => { req.session.loginer = json; res.send({ state: 'success', id: json.id }); }).catch((err) => { res.send({ state: 'error', error }); })
对象修改:
domain.call(`User.${userId}.updatePwd`, { password }, (err, json) => { if (err) { err.state = 'error' res.send(err); } else { res.send({ state: 'success' }); } })
对象的查询:
domain.get('Topic', topicId).then((topic) => { if (topic && topic.authorId === authorId) { domain.call(`Topic.${topicId}.update`, { title, body }, (err, result) => { if (err) { res.send({ state: 'error' }) } else { res.send({ state: 'success' }); } }) } else { res.send({ state: 'error' }); } })
事件监听,通过接受消息来处理数据
domain.on('*', (event) => { let Model = models[event.actorType]; if (event.name === 'create') { Model.create(event.data); } else if (envent.name === 'remove') { Model.remove({ id: event.actorId }) } else { console.log('--------', event.actorId, '------', event.data,'------',event.name); if (event.actorType === 'AdminUser') { let data = {}; switch (event.name) { case 'update': let { userName, email, password, phoneNum, group } = event.data; data = event.data; break; } } Model.update({ id: event.actorId }, data).exec(); } })
PS:cqrs在node全栈中的设计思想还比较新,但是大胆猜测在未来会有广阔的应用空间,值得深入了解下。