MBAPI模型——向统一标准化的努力
MBAPI模型——向统一标准化的努力
CQRS说的是Command和Query分离。它把Action分类为两种:Command和Query。并且明确的说“不存在第三种”。
其实博客园的博主也是分作两类的:一类是男博主,一种是女博主。不存在第三种博主。将博客园的博主按照男女分类为两种的分类法比将Action分类为Command和Query好理解多了吧。
上面的分类也可以表述为:Action分两种,一种是Command,一种是“非”Command;博客园的博主分两种,一种是男博主,一种是“非”男博主。
什么是Command?什么是Query?不必追求100%正确,借助大家熟知的“方法/Method来”来形式化Command和Query的话。那么,所有我们书写过的“无返回值”的方法都是Command,所有我们书写过的“有返回值”的方法都是Query。“有无返回值”跟“是非男女”比较像了吧。比如这里书写一个void SignIn(string loginName, string password),这个方法就属于Command,而AccountInfo GetAccount(string loginName)则属于Query。那么要是有人书写了AccountInfo Login(string loginNamem, string password)方法怎么办?可以将它看作是Command和Query的混合,先Command再Query。
在实现CQRS的框架中我们是如何模型化Command的呢?是这样的,比如对于上面的void SignIn(string loginName, string password)命令方法可以模型化为下面的样子:
public class SignInAccountCommand : Command{
public SignInAccountCommand(string loginName, string password){
this.LoginName = loginName;
this.Password = password;
}
public string LoginName{get; private set;}
public string Password{get; private set;}
}
SignInAccountCommand跟SignIn方法完全一样,都是完整的描述一件事情,并为‘做’提供完整的输入。loginName和password即是输入。SignInAccountCommand成功对象化了SignIn方法并且丝毫没有损失信息。值得说明的是:Command中的输入和Event中的输出都应是只读的不可变的”。
下面我们看看“四人帮”的Command模式是怎么说的:
Command:一个封装了用于处理客户端请求的逻辑对象。这些对象可以立即
执行,为推迟处理而进入队列、保存和记录日志。
Command Message:标识客户端需要调用的逻辑操作的消息。命令消息还为
这些操作提供输入数据。
Command完整的描述了一件事情并为做提供了完整的输入。当我们使用命令式编程语言比如C#写程序的时候就是在书写一个个的命令。比如我们的rdb中有个Function表,有个引用了Function表的ManagedFunction表,还有个引用了ManagedFunction表的RoleFunction表。当我们在数据访问层或者应用服务层使用C#书写删除Function实体的逻辑的时候是怎么做的呢?可以使用如下三个命令来表达:DeleteRoleFunctionCommand,DeleteManagedFunctionCommand和DeleteFunctionCommand。将这三个命令按照顺序执行就可以了。
public class SignInAccountCommand : Command{
public SignInAccountCommand(string loginName, string password){
this.LoginName = loginName;
this.Password = password;
}
public string LoginName{get; private set;}
public string Password{get; private set;}
}
跟
void SignIn(string loginName, string password)是等效的。但是为每一个命令方法都创建一个command class看起来不太像是最佳实践。应该有更好的等效的表达命令的方式。可不可以这样?引入几个简单的概念,然后基于对这些简单概念的理解给出一个“形”。使用这个形应可以表达任何命令。现在我们可以将SignIn方法表述成这样:
ontology=”account”
verb=”signIn”
infoID=[{‘key’:’loginName’,’value’:’anycmd’}]
infoValue=[{‘key’:’loginName’,’value’:’anycmd’},{‘key’:’password’,’value’:’123456’}]
看来是可以的,因为通过以上四行我们同样完整的描述了SignIn命令并为执行提供了完整的输入。上面这种形式也并不太难理解,对比一下http的请求文档两者没有本质的区别。Ontology + infoID = uri,verb = httpMethod,infoValue = httpContent。
既然使用http也能描述命令为什么还要提出新的形式呢?这个不成熟的随笔上有回答。
Ontology、verb、infoID、infoValue是Command的要素,除此之外还有一些辅助元素,如下图所示:
采用上图的“形”填入承载“信息”的数据是可以描述宇宙万事万物的。
Message分三种:Action(行动)、Command(命令)、Event(事件)。三者在数据传输对象的结构上不做进一步区分,三者结构完全相同。
时间戳:三者均具有时间戳属性,不同的是“行动”的时间戳来自当前的宇宙上下文;命令的时间戳由客户端填入,但命令的具体执行时间由服务端决定;事件的时间戳往往是过去的也由客户端填入。
执行性质:服务端立即执行请求类型为Action的命令,随意执行请求类型为Command的命令(“随意”指何时执行由服务端自定,如延迟到晚上执行),如何处理请求类型为Event的命令也由服务端自主决定(面向EventSourceType、EventSubjectCode和StateCode编程)。
服务器只有一个收发这三种Message的服务就可以了:
Message可以完整的描述任何一件事情并为做提供完整的输入,服务的inputModel和outputModel类型都是Message的话就功能完备了。