經過一段時間的準備,新的一期【ASP.NET Core MVC開發實戰之商城系統】已經開始,在之前的文章中,講解了商城系統的整體功能設計,頁面佈局設計,環境搭建,系統配置,及首頁【商品類型,banner條,友情鏈接,降價促銷,新品爆款】等功能的開發,今天繼續講解商品列表頁面功能開發,僅供學習分享使用... ...
EF幾乎是按照領域的概念誕生,它可以和Clean結合。在 微服務架構 中服務的通用開發方式。當有了統一開發方式後,協作將更上一層樓。
最近沒工作,就寫了這章。是為了團隊協作的,如何能被更多知道同樣知識的人快速瞭解業務系統?
通用名詞:DDD中定義了通用名詞,意為某件事情的專業術語,軟體中也可擁有。Clean和DDD的戰術設計近乎一致。
先看看Clean中的概念,簡略意思是低耦合高內聚。每一個方法都需要遵循SOLID,命名為特殊的含義。從方法名中能看到它的功能,即使它的內部很複雜。都能通過幾句簡短的代碼,飛快的看到過程。過程中隱藏著複雜細節,不需要及時的都去瞭解,就能理解這個方法都做了什麼事情。
低耦合是代表不同的模塊之間更改沒有衝突。
高內聚是代表上層不同的模塊調用下層相同的模塊,這個下層模塊更改,多個上層模塊也被更改。
EF中DbContext,則符合領域的概念代表一個上下文,它被設計的很靈活,DbContext是一個倉儲管理者(意為DDD中的倉儲模式,多個倉儲的管理者)。用DbContext可以直接調用出多個不同倉儲,用linq簡單的方式操作倉儲的API。從操作倉儲API中可以操作多個實體(意為DDD中的聚合根模式)。
如圖所示(剪頭方向代表正被它使用的):
實體
Entity 實體是與資料庫接觸的模型體,它可以是存儲在記憶體資料庫中的緩存,也可以是關係型資料庫中的表。
每一個實體有自己的行為方法。不過有些特殊的實體,卻能管理其他實體的行為,它是實體管理者也叫聚合根。實體管理者它被任命管理它所認知範圍內所有的實體。實體管理者不能處理過程業務,過程業務只能是管理者實現。
不可變的值
實體中很重要的值對象,在實體中通常會有要改一起改的屬性,它們不能只改一個,是一個整體。這種一般會把它做成結構體,在實體中做成結構屬性,以防止對它進行破壞。
管理者
Manager 是遵循SOLID的好管理者。它指的是只做有意義的事情,在代碼層面是獨一無二的,它可以方便上層調用最少量代碼。一直在上層編寫可能會有很多重覆功能,更改其一偶爾忽略其二的事情常常發生,它的出現將改變這一切。
它是範圍生命周期的服務,它會使用工廠,使用緩存,使用外部服務,DbContext,日誌等項。它將暴露只屬於它的倉儲,而不是DbContext(DbContext 可以切換不同的關係型資料庫,使得倉儲不用改變),還將暴露對上層有意義的方法和屬性。它的方法不能隱藏業務過程,只能隱藏代碼和業務細節。它的方法命名必須簡明要義,必須讓外部來看瞬間理解其意思,而不是添加更多的註釋。
模型
模型和實體一樣重要,它可以是分頁帶很多查詢參數的請求模型、外部服務的模型、工廠的模型等等。它不是資料庫交互的模型體,它也可以包含行為。
外部服務
外部服務應該是瞬態生命周期可以有服務註冊選擇項,最常包含的有鏈路追蹤,單例HttpClient。只能被管理者調用,這是因為在上層不需要處理細節。
實體規則
瞭解實體的行為方法後,還需要瞭解實體上有些行為是不允許的會報錯。通常是有屬性不能為空,字元串長度太長等項。這種應該在領域中呈現,作者自己命名為 實體規則。實體規則能被實體的行為調用,還可以被數據傳輸對象的規則調用。實體規則是被註入為單例的服務,實體規則可以有服務註冊選擇項。實體規則的方法要簡明要義,請求參數必須有是否拋出異常的選項,返回值必須是bool類型。
數據傳輸對象
是Rpc的過程傳輸對象,一般有請求參數和返回參數。數據傳輸對象不能直接是實體,因為不易於前端的使用,可能會有前端不認識的屬性出現。請求參數,每個API都必須不一樣,返回參數,則相反儘量使用同樣的返回參數,請求和返回都應該用最少的屬性滿足,這樣設計有易於前端的使用。一般命名規則為,請求參數動詞在前名詞在中間最後加上Dto。返回參數則是名詞加上Dto,嵌套返回參數亦是如此。
映射
幾乎映射規則是實體和數據傳輸對象的映射,一般只服務於Rpc。在管理者中應該儘量減少映射的出現,因為管理者的請求應該只接受實體和工廠模型的傳輸。
數據傳輸對象規則
是最終要返回給用戶的錯誤信息,是單例聲明周期的服務可以有服務註冊選擇項。不可包含DbContext,它應該是無狀態的規則,可以調用專有的實體規則來滿足高內聚。
一般有狀態的規則應該在業務中體現,若是出現問題應該拋出異常返回400 BadRequest,並且不提示任何信息,這很有可能是一個破壞請求,生產環境正常業務下是不會出現400的。
有狀態的規則在業務中體現,必須有專門的API去給前端判斷,比如昵稱是否重覆API,而不是主流程API出現判斷。
Rpc
遠程過程調用API,應該儘可能的用過濾器保護內部業務和數據,這包含請求緩存,限流,授權等項。
它的業務過程應該簡單清晰,包含使用日誌,使用數據傳輸對象規則、映射規則、DbContext和管理者、還有實體等項。
集成事件
只有上層才會有集成事件和外部系統發送事件交互,從而解耦項目之間的依賴。不可否認的是集成事件和Rpc如出一轍,都可以使用相同的戰術,需要保護內部業務和數據。(領域事件是項目內部發生,依賴註入的方式做成的)
單元測試
由於項目是高內聚,低耦合,這使得其中的每個方法,都可以進行單元測試。
單元測試最終的目的是:將這個方法過去錯誤進行保留,當需要修改這個方法時,可以運行過去的錯誤,用來相容新的的業務規範。
其他配置項
應該與業務需要適配,儘量去做出靈活的配置。不應該出現不認識或者無用的配置屬性。知己知彼百戰百勝,配置項不是一定要記住,也不是一定要把註釋寫在配置文件中。正確的做法是,給每一項屬性加上完整的註釋。
規模大成分散式
當知道如何完成一個獨立項目後,後面應該知道如何分散式項目,完成上下文解耦。
舉個例子,有個用戶項目,包含登錄,註冊。還包含,管理員的用戶創建不需要登錄。一方流量大,一方根本沒流量,在同一個項目中。此時是需要完成上下文解耦,一個上下文變成兩個項目。身份項目服務於普通用戶,賬號管理服務於管理員。
我們知道既然慢慢的分佈成了多個項目變成了分散式,就要設計很多東西:日誌收集、指標監控、鏈路追蹤、分散式緩存、緩存淘汰策略、領導選舉、哨兵模式、索引文檔、服務發現、健康檢查、負載均衡、網關、消息隊列、分散式設計模式、Sagas等項。這裡每一項在關鍵時刻就需要上,不上就很難做成,切忌不可操之過急,根據團隊成員分配,微服務是把雙刃劍。
當項目和工具太多又需要承受每個服務的管理,就需要容器化服務。容器越來越多,就需要上管理容器的工具K8s。當資料庫承受不住還要上集群資料庫,都知道集群資料庫不是一般的貴,要是上不起只能跑到消息隊列。
這隻是表象,實際中許多還需要處理存儲圖片,文件,靜態資源,還需要上了對象存儲。資源太卡,還需要CDN。功能變數名稱,DNS等等等很多。每一個知識點的用處都有講究,知識層出不窮很多很多,學不完根本學不完。
最後
借鑒了微軟微服務文檔(https://learn.microsoft.com/zh-cn/dotnet/architecture/microservices/microservice-ddd-cqrs-patterns/)、微軟EF文檔(https://learn.microsoft.com/zh-cn/ef/core/)、《Clean架構》、《領域驅動設計》、《微服務設計模式》。如有異議歡迎探討。