摘要:中的事件的一個,我暫且理解為一個中的和這兩個屬性已經在框架中直接掛載在了對象上,歸功于曾老師。
CQRS是啥?DDD又是啥?
這兩個概念其實沒什么神秘的,當然此文章中的這兩個概念以曾老師的課程為準(關于CQRS和DDD的標準概念,google上已經很多了,不再贅述。)
DDD(Domain Driven Design),領域驅動設計開發(fā)。
DDD和OOP有什么同嗎?其實就我個人經驗來說,沒有任何不同(當然你可以反駁我),DDD就是OOP。這里以曾老師課上的概念為準,domain就是世界,包含了當前所有actor的一個域,這個域是一個上帝視角,可以監(jiān)聽每一個域中發(fā)生的事件,并且記錄。
CQRS,既命令和查詢職責分離(Command Query Responsibility Segregation)。
在普通mvc架構中,對于數(shù)據庫的CRUD基本都是寫在controller層,這樣一來路由非常臃腫,而且維護起來簡直是噩夢。
CQRS將查詢與職責分離。簡單說來,就是寫操作和讀操作分離,讀操作寫在路由中,寫操作通過面向對象寫入類的業(yè)務方法中,這樣路由中的查詢部分薄了,而且對于寫操作的可讀性,重用性和維護性大大提高。
相比較于普通mvc,cqrs分為核心層Core(及核心層擴展Core Extension)應用層(Application),UI層,看起來3層,其實是四層,但是由于核心層與核心層擴展的伸縮性很強,并且針對項目的大小來決定,所以就我覺得用3.5層來描述比較合適。
取cqrs文檔中的例子
const {Actor} = require("cqrs"); module.exports = class User extends Actor{ constructor(data){ const {name} = data; super({ name, createTime: Date.now(), stars:[], // 被關注明星的 ids watchers:[] // 關注者的 ids }); } // 關注某位明星 async follow(starId){ const service = this.service; const star = await service.get("User",starId); if(starId !== this.id && star){ await star.addWatcher(this.id); this.$(starId) } } // 取消關注某位明星 async unFollow(starId){ const star = await this.service.get("User",starId); if(star){ await star.deleteWatcher(this.id); this.$(starId); } } // 加入關注者 watcher addWatcher(watcherId){ if(watcherId !== this.id) this.$(watcherId); } // 取消被關 deleteWatcher(watcherId){ this.$(watcherId); } get updater(){ return { follow(json, event){ const stars = json.stars; stars.push(event.data); return { stars } }, unFollow(json, event){ const stars = json.stars; var set = new Set(stars); set.delete(event.data); return { stars:[...set] } }, addWatcher(json,event){ const watchers = json.watchers; watchers.push(event.data); return { watchers } }, deleteWatcher(json,event){ const watchers = json.watchers; const set = new Set(watchers); set.delete(event.data); return { watchers:[...set] } } } } }
以上例子是一個cqrs (傳送門)Actor的實現(xiàn),通過this.$產生一個事件,事件由updater接收,進行數(shù)據的真正修改。
const {Domain} = require("cqrs"); const User = require("./User"); const domain = new Domain(); // 注冊 User Actor 類 domain.register(User); // 即時異步執(zhí)行函數(shù) (async function () { // 創(chuàng)建用戶1 let user1 = await domain.create("User",{ name:"leo" }); // 創(chuàng)建用戶2 let user2 = await domain.create("User",{ name:"zengliang" }) // user1 關注 user2 await user1.follow(user2.id); console.log(user1.json.stars); // 打印一下 user1 監(jiān)聽所有 ids console.log(user2.json.watchers); // 打印一下 user2 追隨者的所有 ids user1.unFollow(user2.id); // user1 取消關注 user2 // 重新加載 user1 和 user2 user1 = await domain.get("User",user1.id); user2 = await domain.get("User",user2.id); console.log(user1.json.stars); // 打印一下 user1 監(jiān)聽所有 ids console.log(user2.json.watchers); // 打印一下 user2 追隨者的所有 ids })();
以上是在運行中對User實例對象的操作,關注與取關的操作。
Any fool can write code that a computer can understand. Good programmers write code that humans can understand. -- 某位大牛
以上的例子很好的詮釋了可讀性還有重用性。對于寫操作來說,完全用業(yè)務方法來實現(xiàn),那么路由中可以僅包含cqrs中Q的部分,這樣做到了業(yè)務和查詢分離,那么迷惑也開始解開了。
寫操作,用業(yè)務方法來完成,屬于核心層
query,既查詢操作,寫在router中,是應用層變薄
在使用普通mvc的時候,邏輯和查詢通常都會放在路由當中,這樣造成的高耦合性(coupling)讓代碼的重用性,可讀性,可伸縮性很差。維護起來簡直噩夢連連。我的第一個項目是用標準mvc完成,后期加新需求的時候基本山就是牽一發(fā)動全身,也是我的經驗確實不夠對于很多地方沒有對代碼進行可重用的封裝。
現(xiàn)在淺談一下 Auxo(傳送門)Auxo框架集成了Nuxt(Vue),Vuex,Express,cqrs四個重要框架。這樣在開發(fā)時就不用再辛苦搭建開發(fā)環(huán)境了,直截了當。Auxo是約定式的框架,關于文件結構是根據Nuxt(傳送門)的,所以有必要讀一讀Nuxt的文檔,對Nuxt有一定了解之后就可以用了而且上手很快,因為基本上不需要配置什么東西。
在Auxo框架中,數(shù)據遵循Event Sourcing原則,分兩個collection。
一個是事件數(shù)據庫記錄在domain中發(fā)生的所有事件,讓事件回溯、長故事(saga)和事件鎖(lock)成為可能;
另外一個是查詢數(shù)據庫,記錄普通數(shù)據,我自己的理解就是面向數(shù)據庫開發(fā)的那種最基本的數(shù)據庫。
eventstore記錄事件對象的數(shù)據庫,可以通過該數(shù)據庫的數(shù)據進行數(shù)據回溯。
snap事件快照。domain中的事件的一個snapshot,我暫且理解為一個log
server/index.js 中的 req.dbs 和 req.$domain這兩個屬性已經在框架中直接掛載在了req對象上,歸功于曾老師。在server/index.js中,已經定義好了,這個文件相當于express的app.js,只是文件名不一樣。
req.dbs就是上述的查詢數(shù)據庫,可以使用mongojs來query。
req.$domain就是domain,即上帝視角,可以用以下語句
req.$domain.get("User", uid); // 獲取User對象 req.$domain.create("User", {username: "ephraimguo", password:"*******"}); // 創(chuàng)建user對象
等domain對象的方法進行數(shù)據操作。
在Vue組件中的axios 和 domain這兩個對象已經寫在plugins/文件夾里面,可以直接在Vue組件中引用如下
Listener 核心層擴展 (有個小坑)
起初看到曾老師用listener但是不明白怎么監(jiān)聽,而且去看epxress-cqrs的源碼的時候,看到listener的路徑是作參數(shù)與傳入了的。
截取一段express-cqrs的源碼
// Register Actors Class from actors folder ActorList.filter(Actor => /.*.js$/.test(Actor)). forEach(Actor => domain.register(require(path.join(actorPath, Actor)))); // Get Listener from listener folder listeners.filter(listener => /.*.js$/.test(listener)). forEach(listener => require(path.join(listenerPath, listener))(domain));
第一步,在根目錄下添加listener文件夾
第二部,創(chuàng)建新的監(jiān)聽js文件,
Listener 內部寫法,如下(個人經驗)
module.exports = function(domain){ // Utilise domain.on(...) to make onAction listening }
這次先暫時聊這么多,cqrs還有很多好用的方法和思想可以慢慢琢磨,而且這種編程思想易實踐,并且對全局的把控更精準,心有猛虎細嗅薔薇,當然這篇文章也是針對上過曾老師課的童鞋們,不算是掃盲,過后會繼續(xù)寫一些關于cqrs框架應用的文章,也歡迎大家提問,并且一起討論。如果有錯誤,也請大家指正,我會馬上修改。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/98611.html
摘要:它包含多個屬性,這些屬性值叫做元數(shù)據。會根據元數(shù)據渲染組件,并執(zhí)行組件邏輯。元數(shù)據會告訴圖和將這個類處理成一個組件。元數(shù)據這段代碼表示這個組件可以通過這個標簽來調用。 那些年初識Angular 由于工作需要初識了Angular,由于個人在學習一門新語言的時候喜歡買一本相關的書籍自己鉆研,還記得自己的第一本Angular書籍是關于Angular2的學習,自此正式踏入Angular的學習。...
摘要:最近發(fā)現(xiàn)文章老是被竊取,有些平臺舉報了還沒有用。最后不了了之,產品很配合,但是內驅力不強。為什么內驅力不強,因為給他帶來的收益不夠。所以在千個團隊中實行可能有千套不同的方案。最近發(fā)現(xiàn)文章老是被竊取,有些平臺舉報了還沒有用。請識別我的id方丈的寺院。 摘要 DDD領域驅動設計,起源于2004年著名建模專家Eric Evans發(fā)表的他最具影響力的著名書籍:Domain-Driven Design...
摘要:一界面框架是微軟在其最新桌面操作系統(tǒng)中使用的圖形用戶界面。干貨盤點二服務在寫后臺代碼的過程中,經常會遇到要寫一些多帶帶的服務。這個傳統(tǒng)的控件開發(fā)起來很不方面,使用也不友好。發(fā)現(xiàn)有用的,這個第三方的框架,集成的很好,用起來也方便。一、Fluent Ribbon界面框架Fluent/Ribbon是微軟在其最新桌面操作系統(tǒng)Windows 7中使用的圖形用戶界面。 Windows平臺的進化,伴隨著系...
摘要:在這種情況下,每一個微服務定義一個限界上下文,類似于領域驅動的限界上下文。設計你的微服務系統(tǒng)的響應式微服務架構這本書對于微服務系統(tǒng)架構很有幫助。 1.Lagom概念介紹 lagom框架包含一系列的可以支持我們從開發(fā)到部署的庫以及開發(fā)環(huán)境: >在開發(fā)階段,可以通過一個簡單的命令構建我們的項目,啟動所有你的服務,并且可以支持所有的lagom基礎設置層。當你修改了代碼,logom是有熱加載的...
閱讀 2772·2021-11-02 14:42
閱讀 3163·2021-10-08 10:04
閱讀 1184·2019-08-30 15:55
閱讀 1025·2019-08-30 15:54
閱讀 2311·2019-08-30 15:43
閱讀 1680·2019-08-29 15:18
閱讀 863·2019-08-29 11:11
閱讀 2362·2019-08-26 13:52