首页 > 代码库 > 大话CQRS-简单
大话CQRS-简单
前言:
看了最新的《老友记》学到了一句“装逼格”的句子,真正优秀的事物包含了:简单,美好,普适。
最近在研究CQRS,争取用到项目中,当然为了完善,体悟CQRS项目也是在一步步的完善中。
舒畅感
第一篇说的是简单,当然简单并不是指CQRS本身的概念,而是仅仅作为一个初学者在尝试搭架子的过程当中突然接触到CQ的想法时那种醍醐灌顶的舒畅感。
唐僧:喂喂喂!大家不要生气,生气会犯了嗔戒的!悟空你也太调皮
了,我跟你说过叫你不要乱扔东西,你怎么又…你看我还没说
完你又把棍子给扔掉了!月光宝盒是宝物,你把他扔掉会污染
环境,要是砸到小朋友怎么办?就算砸不到小朋友砸到那些花
花草草也是不对的!
......
悟空:所以呢我就抓住苍蝇挤破它的肚皮把它的肠子扯出来再用它的
肠子勒住他的脖子用力一拉,呵--!整条舌头都伸出来啦!
我再手起刀落哗--!整个世界清净了。现在大家明白,为什
么我要杀他!
当我们把太多的时间用在研究概念上时,妄想通过读二手文章揣摩大牛心态去了解CQRS思想时就是遇到唐僧的赶脚,要死啊!
但是我们先以CQ的思想去尝试一个简单而又普适的小项目时,整个世界清静了。
贱解
以“贱贱”的心态说一下我对于CQRS的理解。
上面是包括了理解与问题,问题是为了之后的文章或者园友能够帮忙解答一下下,回归正题先说理解吧。
了解CQRS需要对里面的关键词做了解。
C:
Command:每一种行为除Q以外的都可以看做Command;
Command Bus: 命令总线,所有的Command将会被发送到CommandBus中;
Command Handler:每一种Command最后会有对应的CommandHandler进行处理;
Domain:由于Command最终需要落实到业务Model上做操作,所以Domain是真正去处理业务Command的地方;
Event:Domain中对于业务Command最终会分解成多种对于聚合根的Event;
Repository:在Domain分发处理之后持久Event;
EventSource: 被持久的Event集合,可以InMemory也可以放在关系DB中;
Event Bus:类似CommandBus去同步(或异步)的方式装载Event的最后状态;
Event Handler:类似于CommandHandler单独对Event最处理;
Read DB:最终修改DB状态,达到数据一致的目的,当然ReadDB可以以NoSql的方式对Q端进行优化;
Message: 暂时不太明白;
Q:
Q端感觉没什么可以说的,就是建立一套方便业务Query与ReportDB之间的管道,可以专门对于Query进行优化而不影响整个的业务逻辑(C端那些行为)。从而达到了在业务,代码结构上的读写分离。
没有代码就是耍流氓
最后奉上一段小Demo作为CQRS进化的初级版,后续对于CQRS的理解及应用的进化也会发表上来。
结构图:
是一段Web程序:
QueryProcess:负责处理Q端,下层以T-SQL,EF去实现;
DataProcess:负责C端,下层以EF实现;
Model:包含了CommandModel,ViewModel,DTOModel;
Data:包含EF及Repository;
Common:一些基础通用的Helper类。
API:是和另外两个系统以Restful做交互的;
Controller:
[HttpGet] [UserAuthentication] public ActionResult Index(string id) { AccountViewModel vm = new AccountViewModel(); vm.roleList = ServiceLoader.RoleQueryProcess.FetchAll(null).ToList(); vm.accountDtoList = ServiceLoader.AccountQueryProcess.FetchAllToDto().ToList(); if (!string.IsNullOrEmpty(id)) { vm.tag = id; } return View(vm); }
通过外观模式就(ServiceLoader)获取对应的Query;
QueryProcess:
string sql = string.Format(@"select top {0} ID ,RoleName ,RoleCode ,ParentId from Tb_Role a where 1=1 {1} and id not in (select top {2} id from Tb_Role where 1=1 {1} order by id desc) order by id desc" , PageConfig.ShowCount , "" , PageConfig.ShowCount * (PageConfig.RolePageNow - 1)); DataTable queryTable = SqlHelper.GetTableText(sql, null)[0]; string json = JsonConvert.SerializeObject(queryTable); return JsonConvert.DeserializeObject<List<AutodeskTool.Data.Tb_Role>>(json);
通过T-Sql方式进行DB的Query,当然中间可以嫁接上Cache;
Command:
public class RegisterCommand:BaseCommand { public string userName { get; set; } public string password { get; set; } public bool isRemember { get; set; } }
一个简单的Command产生;
[HttpPost] public ActionResult Login(SignInCommand sign) { Tb_Account account = new Tb_Account() { UserName = sign.userName, Password = sign.password }; CommandActionResult commentResult = ServiceFactory.AccountService.AccountLogin(account); if (commentResult.isOk) { //... } }
将Command同样通过外观模式的ServiceFactory通过反射分发到真的Handler程序(AccountService);
Domain:
public CommandActionResult AccountLogin(Tb_Account account) { try { Tb_Account result = AccountRepository.FetchT(a => a.UserName.Equals(account.UserName) && a.Password.Equals(account.Password));}cache{//...}}
由于还不太清楚EventSource的真正合理的用法这里我就直接将Command的聚合根持久到DB了;
由于还是对于CQRS的使用与理解上还处于初级阶段,但是当接触这一概念是还是有种很舒服的感觉,不完善的地方还请大家包含,也希望大牛能够指点,后续还会带来。
thanks.
大话CQRS-简单