DDD分層架構最佳實踐

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"還在單體應用的時候就是分層架構一說,我們用得最多的就是三層架構。而現在已經是微服務時代,在微服務架構模型比較常用的有幾個,例如:整潔架構,CQRS(命令查詢分離)以及六邊形架構。每種架構模型都有自己的應用場景,但其核心都是“","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"高內聚低耦合","attrs":{}},{"type":"text","text":"”原則。而運用","attrs":{}},{"type":"text","marks":[{"type":"bgcolor","attrs":{"color":"#F6F6F6","name":"user"}}],"text":"領域驅動設計","attrs":{}},{"type":"text","text":"(DDD)理念以應對日常加速的業務變化對架構的影響,架構的邊界越來越清晰,各司其職,這也符合微服務架構的設計思想。以領域驅動設計(DDD)爲理念的分層架構已經成爲微服務架構實踐的最佳實踐方法。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"一、什麼是DDD分層架構","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"1. 傳統三層架構","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"要了解DDD分層架構,首先先了解傳統的三層架構。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/58/5892fec13e62ae0a4aabb208c6b7f247.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"傳統三層架構流程:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"第一步考慮的是數據庫設計,數據表如何建,表之間的關係如何設計","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"第二步就是搭建數據訪問層,如選一個ORM框架或者拼接SQL操作","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"第三步就是業務邏輯的實現,由於我們先設計了數據庫,我們整個的思考都會圍繞着數據庫,想着怎麼寫才能把數據正確地寫入數據庫中,這時CRUD的標準作法就出現了,也就沒有太多考慮面向對象,解耦的事情了,這樣的代碼對日常的維護自然是越來越困難的","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"第四步表示層主要面向用戶的輸出","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"2. DDD分層架構","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/c5/c5cd3f68321670cb83242b9ee3a79efd.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"爲了解決高耦合問題並輕鬆應對以後的系統變化,我們提出了運用","attrs":{}},{"type":"text","marks":[{"type":"bgcolor","attrs":{"color":"#F6F6F6","name":"user"}}],"text":"領域驅動設計","attrs":{}},{"type":"text","text":"的理念來設計架構。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"bgcolor","attrs":{"color":"#F6F6F6","name":"user"}}],"text":"此段落部分總結來源於歐創新《DDD實踐課》的《07 | DDD分層架構:有效降低層與層之間的依賴》讀後感","attrs":{}}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"1)領域層","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"首先我們拋開數據庫的困擾,","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"先從業務邏輯入手開始","attrs":{}},{"type":"text","text":",設計時不再考慮數據庫的實現。將以前的業務邏輯層(BLL)拆分成了","attrs":{}},{"type":"text","marks":[{"type":"bgcolor","attrs":{"color":"#F6F6F6","name":"user"}}],"text":"領域層","attrs":{}},{"type":"text","text":"和","attrs":{}},{"type":"text","marks":[{"type":"bgcolor","attrs":{"color":"#F6F6F6","name":"user"}}],"text":"應用層","attrs":{}},{"type":"text","text":"。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"領域層聚焦業務對象的業務邏輯實現,體現現實世界業務的邏輯變化。它用來表達業務概念、業務狀態和業務規則,對於業務分析可參照:《使用領域驅動設計分析業務》","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"2)應用層","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"應用層是領域層的上層,依賴領域層,是各聚合的協調和編排,原則上是不包括任何業務邏輯。它以較粗粒度的封閉爲前端接口提供支持。除了提供上層調用外,還可以包括事件和消息的訂閱。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"3) 用戶接口層","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"用戶接口層面向用戶訪問的數據入向接口,可按不同場景提供不一樣的用戶接口實現。面向Web的可使用http restful的方式提供服務,可增加安全認證、權限校驗,日誌記錄等功能;面向微服務的可使用RPC方式提供服務,可增加限流、熔斷等功能。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"4) 基礎設施層","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"基礎設施層是數據的出向接口,封裝數據調用的技術細節。可爲其它任意層提供服務,但爲了解決耦合的問題採用了依賴倒置原則。其它層只依賴基礎設施的接口,於具體實現進行分離。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"二、DDD分層代碼實現","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"1. 結構模型","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/bf/bfff156e20d9b904d8fb8ecb2ae26573.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"2. 目錄結構","attrs":{}}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":".\n├── pom.xml\n└── src\n ├── main\n │   ├── java\n │   │   └── fun\n │   │   └── barryhome\n │   │   └── ddd\n │   │   ├── WalletApplication.java\n │   │   ├── application\n │   │   │   ├── TradeEventProcessor.java\n │   │   │   ├── TradeMQReceiver.java\n │   │   │   └── TradeManager.java\n │   │   ├── constant\n │   │   │   └── MessageConstant.java\n │   │   ├── controller\n │   │   │   ├── TradeController.java\n │   │   │   ├── WalletController.java\n │   │   │   └── dto\n │   │   │   └── TradeDTO.java\n │   │   ├── domain\n │   │   │   ├── TradeService.java\n │   │   │   ├── TradeServiceImpl.java\n │   │   │   ├── enums\n │   │   │   │   ├── InOutFlag.java\n │   │   │   │   ├── TradeStatus.java\n │   │   │   │   ├── TradeType.java\n │   │   │   │   └── WalletStatus.java\n │   │   │   ├── event\n │   │   │   │   └── TradeEvent.java\n │   │   │   ├── model\n │   │   │   │   ├── BaseEntity.java\n │   │   │   │   ├── TradeRecord.java\n │   │   │   │   └── Wallet.java\n │   │   │   └── repository\n │   │   │   ├── TradeRepository.java\n │   │   │   └── WalletRepository.java\n │   │   └── infrastructure\n │   │   ├── TradeRepositoryImpl.java\n │   │   ├── WalletRepositoryImpl.java\n │   │   ├── cache\n │   │   │   └── Redis.java\n │   │   ├── client\n │   │   │   ├── AuthFeignClient.java\n │   │   │   └── LocalAuthClient.java\n │   │   ├── jpa\n │   │   │   ├── JpaTradeRepository.java\n │   │   │   └── JpaWalletRepository.java\n │   │   └── mq\n │   │   └── RabbitMQSender.java\n │   └── resources\n │   ├── application.properties\n │   └── rabbitmq-spring.xml\n └── test\n └── java\n\n\n\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"此結構爲單一微服務的簡單結構,各層在同一個模塊中。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在大型項目開發過程中,爲了達到核心模塊的權限控制或更好的靈活性可適當調整結構,可參考《 數字錢包系統》系統結構","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"3. 領域層實現(domain)","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在業務分析(《使用領域驅動設計分析業務》)之後,開始編寫代碼,首先就是寫領域層,創建","attrs":{}},{"type":"text","marks":[{"type":"bgcolor","attrs":{"color":"#F6F6F6","name":"user"}}],"text":"領域對象","attrs":{}},{"type":"text","text":"和","attrs":{}},{"type":"text","marks":[{"type":"bgcolor","attrs":{"color":"#F6F6F6","name":"user"}}],"text":"領域服務接口","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"1)領域對象","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這裏的領域對象包括實體對象、值對象。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"實體對象","attrs":{}},{"type":"text","text":":具有唯一標識,能單獨存在且可變化的對象","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"值對象","attrs":{}},{"type":"text","text":":不能單獨存在或在邏輯層面單獨存在無意義,且不可變化的對象","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"聚合","attrs":{}},{"type":"text","text":":多個對象的集合,對外是一個整體","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"聚合根","attrs":{}},{"type":"text","text":":聚合中可代表整個業務操作的實體對象,通過它提供對外訪問操作,它維護聚合內部的數據一致性,它是聚合中對象的管理者","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"代碼示例:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"// 交易\npublic class TradeRecord extends BaseEntity {\n /**\n * 交易號\n */\n @Column(unique = true)\n private String tradeNumber;\n /**\n * 交易金額\n */\n private BigDecimal tradeAmount;\n /**\n * 交易類型\n */\n @Enumerated(EnumType.STRING)\n private TradeType tradeType;\n /**\n * 交易餘額\n */\n private BigDecimal balance;\n /**\n * 錢包\n */\n @ManyToOne\n private Wallet wallet;\n\n /**\n * 交易狀態\n */\n @Enumerated(EnumType.STRING)\n private TradeStatus tradeStatus;\n\n \t@DomainEvents\n public List domainEvents() {\n return Collections.singletonList(new TradeEvent(this));\n }\n}\n\n// 錢包\npublic class Wallet extends BaseEntity {\n\n /**\n * 錢包ID\n */\n @Id\n private String walletId;\n /**\n * 密碼\n */\n private String password;\n /**\n * 狀態\n */\n @Enumerated(EnumType.STRING)\n private WalletStatus walletStatus = WalletStatus.AVAILABLE;\n /**\n * 用戶Id\n */\n private Integer userId;\n /**\n * 餘額\n */\n private BigDecimal balance = BigDecimal.ZERO;\n\n}\n\n\n\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"從","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"錢包交易","attrs":{}},{"type":"text","text":"例子的系統設計中,錢包的任何操作如:充值、消息等都是通過交易對象驅動錢包餘額的變化","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"交易對象","attrs":{}},{"type":"text","text":"和","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"錢包對象","attrs":{}},{"type":"text","text":"均爲","attrs":{}},{"type":"text","marks":[{"type":"bgcolor","attrs":{"color":"#F6F6F6","name":"user"}}],"text":"實體對象","attrs":{}},{"type":"text","text":"且組成","attrs":{}},{"type":"text","marks":[{"type":"bgcolor","attrs":{"color":"#F6F6F6","name":"user"}}],"text":"聚合","attrs":{}},{"type":"text","text":"關係,","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"交易對象","attrs":{}},{"type":"text","text":"是錢包交易業務模型的","attrs":{}},{"type":"text","marks":[{"type":"bgcolor","attrs":{"color":"#F6F6F6","name":"user"}}],"text":"聚合根","attrs":{}},{"type":"text","text":",代表聚合向外提供調用服務","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"經過分析","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"交易對象","attrs":{}},{"type":"text","text":"與","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"錢包對象","attrs":{}},{"type":"text","text":"爲1對多關係(@ManyToOne),這裏採用了","attrs":{}},{"type":"text","marks":[{"type":"bgcolor","attrs":{"color":"#F6F6F6","name":"user"}}],"text":"JPA","attrs":{}},{"type":"text","text":"做","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"ORM","attrs":{}},{"type":"text","text":"架構,更多JPA實踐請參考>>","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這裏的領域建模使用的是","attrs":{}},{"type":"text","marks":[{"type":"bgcolor","attrs":{"color":"#F6F6F6","name":"user"}}],"text":"貧血模型","attrs":{}},{"type":"text","text":",結構簡單,職責單一,相互隔離性好但缺乏面向對象設計思想,關於領域建模可參考《領域建模的貧血模型與充血模型》","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"domainEvents()爲","attrs":{}},{"type":"text","marks":[{"type":"bgcolor","attrs":{"color":"#F6F6F6","name":"user"}}],"text":"領域事件","attrs":{}},{"type":"text","text":"發佈的一種實現,作用是","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"交易對象","attrs":{}},{"type":"text","text":"任何的數據操作都將觸發事件的發佈,再配合","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"事件訂閱","attrs":{}},{"type":"text","text":"實現","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"事件驅動設計","attrs":{}},{"type":"text","text":"模型,當然也可以有別的實現方式","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"2)領域服務","attrs":{}}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"/**\n * Created on 2020/9/7 11:40 上午\n *\n * @author barry\n * Description: 交易服務\n */\npublic interface TradeService {\n\n /**\n * 充值\n *\n * @param tradeRecord\n * @return\n */\n TradeRecord recharge(TradeRecord tradeRecord);\n\n /**\n * 消費\n *\n * @param tradeRecord\n * @return\n */\n TradeRecord consume(TradeRecord tradeRecord);\n}\n\n\n\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"先定義服務接口,接口的定義需要遵循","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"現實業務的操作","attrs":{}},{"type":"text","text":",切勿以程序邏輯或數據庫邏輯來設計定義出增刪改查","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"主要的思考方向是交易對象對外可提供哪些服務,這種服務的定義是","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"粗粒度","attrs":{}},{"type":"text","text":"且","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"高內聚","attrs":{}},{"type":"text","text":"的,切勿將某些具體代碼實現層面的方法定義出來","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"接口的輸入輸出參數儘量考慮以對象的形式,充分兼容各種場景變化","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"關於前端需要的複雜查詢方法可不在此定義,一般情況下查詢並非是一種領域服務且沒有數據變化,可單獨處理","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"領域服務的實現主要關注邏輯實現,切勿包含技術基礎類代碼,比如緩存實現,數據庫實現,遠程調用等","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"3)基礎設施接口","attrs":{}}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"public interface TradeRepository {\n /**\n * 保存\n * @param tradeRecord\n * @return\n */\n TradeRecord save(TradeRecord tradeRecord);\n\n /**\n * 查詢訂單\n * @param tradeNumber\n * @return\n */\n TradeRecord findByTradeNumber(String tradeNumber);\n\n /**\n * 發送MQ事件消息\n * @param tradeEvent\n */\n void sendMQEvent(TradeEvent tradeEvent);\n\n /**\n * 獲取所有\n * @return\n */\n List findAll();\n}\n\n\n\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"基礎設施接口放在領域層主要的目的是減少領域層對基礎設施層的依賴","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"接口的設計是不可暴露實現的技術細節,如不能將拼裝的SQL作爲參數","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"4. 應用層實現(application)","attrs":{}}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"// 交易服務\n@Component\npublic class TradeManager {\n\n private final TradeService tradeService;\n public TradeManager(TradeService tradeService) {\n this.tradeService = tradeService;\n }\n\n\n // 充值\n @Transactional(rollbackFor = Exception.class)\n public TradeRecord recharge(TradeRecord tradeRecord) {\n return tradeService.recharge(tradeRecord);\n }\n\n\n // 消費\n @Transactional(rollbackFor = Exception.class)\n public TradeRecord consume(TradeRecord tradeRecord) {\n return tradeService.consume(tradeRecord);\n }\n}\n\n// 交易事件訂閱\n@Component\npublic class TradeEventProcessor {\n\n @Autowired\n private TradeRepository tradeRepository;\n\n @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT, condition = \"# tradeEvent.tradeStatus.name() == 'SUCCEED'\")\n public void TradeSucceed(TradeEvent tradeEvent) {\n tradeRepository.sendMQEvent(tradeEvent);\n }\n}\n\n// 交易消息訂閱\n@Component\npublic class TradeMQReceiver {\n\n @RabbitListener(queues = \"ddd-trade-succeed\")\n public void receiveTradeMessage(TradeEvent tradeEvent){\n System.err.println(\"========MQ Receiver============\");\n System.err.println(tradeEvent);\n }\n}\n\n\n\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"應用服務","attrs":{}},{"type":"text","text":":","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"應用層是很薄的一層,主要用於調用和組合領域服務,切勿包含任何業務邏輯","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可包括少量的流程參數判斷","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"由於可能是多個領域服務組合操作調用,如果存在原子性要求可以增加**@Transactional**事務控制","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"事件訂閱","attrs":{}},{"type":"text","text":":","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"事件訂閱是進程內多個領域操作協作解耦的一種實現方式,它也是進程內所有後續操作的接入口","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"它與應用服務的組合操作用途不一樣,組合是根據場景需求可增可減,但事件訂閱後的操作是相對固化的,主要是滿足邏輯的一致性要求","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"TransactionPhase.AFTER_COMMIT","attrs":{}},{"type":"text","text":"配置是在前一操作事務完成後再調用,從而減少後續操作對前操作的影響","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"事件訂閱可能會有多個消息主體,爲了方便管理最好統一在一個類裏處理","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"MQ消息發佈一般放在事件訂閱中","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"消息訂閱","attrs":{}},{"type":"text","text":":","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"消息訂閱是多個微服務間協作解耦的一步實現方式","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"消息體儘量以統一的對象包裝進行傳遞,降低對象異構帶來的處理難度","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"5. 基礎設施層(infrastructure)","attrs":{}}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"@Repository\npublic class TradeRepositoryImpl implements TradeRepository {\n\n private final JpaTradeRepository jpaTradeRepository;\n private final RabbitMQSender rabbitMQSender;\n private final Redis redis;\n\n public TradeRepositoryImpl(JpaTradeRepository jpaTradeRepository, RabbitMQSender rabbitMQSender, Redis redis) {\n this.jpaTradeRepository = jpaTradeRepository;\n this.rabbitMQSender = rabbitMQSender;\n this.redis = redis;\n }\n\n @Override\n public TradeRecord save(TradeRecord tradeRecord) {\n return jpaTradeRepository.save(tradeRecord);\n }\n\n /**\n * 查詢訂單\n */\n @Override\n public TradeRecord findByTradeNumber(String tradeNumber) {\n TradeRecord tradeRecord = redis.getTrade(tradeNumber);\n if (tradeRecord == null){\n tradeRecord = jpaTradeRepository.findFirstByTradeNumber(tradeNumber);\n // 緩存\n redis.cacheTrade(tradeRecord);\n }\n\n return tradeRecord;\n }\n\n /**\n * 發送事件消息\n * @param tradeEvent\n */\n @Override\n public void sendMQEvent(TradeEvent tradeEvent) {\n // 發送消息\n rabbitMQSender.sendMQTradeEvent(tradeEvent);\n }\n\n /**\n * 獲取所有\n */\n @Override\n public List findAll() {\n return jpaTradeRepository.findAll();\n }\n}\n\n\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"基礎設施層是數據的輸出向,主要包含數據庫、緩存、消息隊列、遠程訪問等的技術實現","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"基礎設計層對外隱藏技術實現細節,提供粗粒度的數據輸出服務","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"數據庫操作:領域層傳遞的是數據對象,在這裏可以按數據表的實現方式進行拆分實現","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"6. 用戶接口層(controller)","attrs":{}}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"@RequestMapping(\"/trade\")\n@RestController\npublic class TradeController {\n\n @Autowired\n private TradeManager tradeManager;\n\n @Autowired\n private TradeRepository tradeRepository;\n\n @PostMapping(path = \"/recharge\")\n public TradeDTO recharge(@RequestBody TradeDTO tradeDTO) {\n return TradeDTO.toDto(tradeManager.recharge(tradeDTO.toEntity()));\n }\n\n @PostMapping(path = \"/consume\")\n public TradeDTO consume(@RequestBody TradeDTO tradeDTO) {\n return TradeDTO.toDto(tradeManager.consume(tradeDTO.toEntity()));\n }\n\n @GetMapping(path = \"/{tradeNumber}\")\n public TradeDTO findByTradeNumber(@PathVariable(\"tradeNumber\") String tradeNumber){\n return TradeDTO.toDto(tradeRepository.findByTradeNumber(tradeNumber));\n }\n\n}\n\n\n\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"用戶接口層面向終端提供服務支持","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可根據不同的場景單獨一個模塊,面向Web提供http restful,面向服務間API調用提供RPG支持","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"爲Web端提供身份認證和權限驗證服務,VO數據轉換","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"爲API端提供限流和熔斷服務,DTO數據轉換","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"將數據轉換從應用層提到用戶接口層更方便不同場景之前的需求變化,同時也保證應用層數據格式的統一性","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"7. 複雜數據查詢","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"以上可見並沒有涉及複雜數據查詢問題,此問題不涉及業務邏輯處理所以不應該放在領域層處理。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果複雜數據查詢需求較多可採用","attrs":{}},{"type":"text","marks":[{"type":"bgcolor","attrs":{"color":"#F6F6F6","name":"user"}}],"text":"CQRS","attrs":{}},{"type":"text","text":"模式,將查詢單獨一個模塊處理。如果較少可由基礎設施層做數據查詢,應用層做數據封裝,用戶接口層做數據調用","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"JPA不太適合做多表關聯的數據庫查詢操作,可使用其它的靈活性較高的ORM架構","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在大數據大併發情況下,多表關聯會嚴重影響數據庫性能,可以考慮做","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"寬表查詢","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"三、綜述","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"DDD分層主要解決各層之間耦合度問題,做到各層各施其職互不影響。各層中領域層的設計是整個系統的中樞,最能體現","attrs":{}},{"type":"text","marks":[{"type":"bgcolor","attrs":{"color":"#F6F6F6","name":"user"}}],"text":"領域驅動設計","attrs":{}},{"type":"text","text":"的核心思想。它的良好設計是保證往後架構的可持續性,可維護性。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"原文鏈接:https://my.oschina.net/barryhome/blog/4913300","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果覺得本文對你有幫助,可以關注一下我公衆號,回覆關鍵字【面試】即可得到一份Java核心知識點整理與一份面試大禮包!另有更多技術乾貨文章以及相關資料共享,大家一起學習進步!","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/20/20d8e05b028b03ea8419f639ab2bae6f.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章