261851413457170.png


最近在熟悉曾老师的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全栈中的设计思想还比较新,但是大胆猜测在未来会有广阔的应用空间,值得深入了解下。