1. 程式人生 > >【DDD】領域驅動設計實踐 —— 框架實現

【DDD】領域驅動設計實踐 —— 框架實現

本文主要了在社群服務系統(ECO)中基於SpringMVC+mybatis框架對DDD的落地實現。本文為系列文章中的其中一篇,其他內容可參考:通過業務系統的重構實踐DDD。

框架實現圖

該框架實現基本和DDD的指導思想契合,主要分為四層,且將關注點放在了domain層。下面將逐層介紹各個元件的職責。

框架詳述

User Interface層

門面層,對外以各種協議提供服務,該層需要明確定義支援的服務協議、契約等。包含:

dto

包括request和response兩部分,通過它定義入參和出參的契約,在dto層可以使用基礎設施曾的validation元件完成入參格式校驗;

controller

支援不同訪問協議的控制器實現,比如:http/restful風格、tcp/二進位制流協議、mq訊息/json物件等等。

controller使用基礎設施層公共元件完成許多通用的工作:

  • 呼叫RequestMapping(SpringMVC公共元件)完成servlet路由;
  • 呼叫checklogin完成登入態/許可權校驗;
  • 呼叫logging元件完成日誌記錄;
  • 呼叫message-resource元件完成錯誤資訊轉義,支援I18N;

application層

service

應用服務層,組合domain層的領域物件和基礎設施層的公共元件,根據業務需要包裝出多變的服務,以適應多變的業務服務需求。

應用服務層主要訪問domain領域物件,完成服務邏輯的包裝。

應用服務層也會訪問基礎設施層的公共元件,如rabbitmq,完成領域訊息的生產等。

assembler

組裝器,負責將多個domain領域物件組裝為需要的dto物件,比如查詢帖子列表,需要從Post(帖子)領域物件中獲取帖子的詳情,還需要從User(使用者)領域物件中獲取使用者的基本資訊。

組裝器中不應當有業務邏輯在裡面,主要負責格式轉換、欄位對映等職責。

domain層

業務領域層,是我們最應當關心的一層,也是最多變的一層,需要保證這一層是高內聚的。確保所有的業務邏輯都留在這一層,而不會遺漏到其他層。按照ddd(domain driven design)理論,主要有如下概念構成:

domain entity

領域實體。有唯一標識,可變的業務實體物件,它有著自己的生命週期。比如社群這一業務領域中,‘帖子’就是一個業務實體,它需要有一個唯一性業務標識表徵,同時他的狀態和內容可以不斷髮生變化。

domain value object

領域值物件。可以沒有唯一性業務標識,且一旦定義,他是不可變的,它通常是短暫的。這和java中的值物件(基本型別和String型別)類似。比如社群業務領域中,‘帖子的置頂資訊’可以理解為是一個值物件,不需要為這一值物件定義獨立的業務唯一性標識,直接使用‘帖子id‘便可表徵,同時,它只有’置頂狀態‘和’置頂位置‘,一旦其中一個屬性需要發生變化,則重建值物件並賦值給’帖子‘實體的引用,不會對領域帶來任何負面影響。

domain factory

領域物件工廠。用於複雜領域物件的建立/重建。重建是指通過respostory載入持久化物件後,重建領域物件。

domain service

領域服務。區別於應用服務,他屬於業務領域層。

可以認為,如果某種行為無法歸類給任何實體/值物件,則就為這些行為建立相應的領域服務即可。比如:轉賬服務(transferService),需要操作借方/貸方兩個賬戶實體。

傳統意義上的util static方法中,涉及到業務邏輯的部分,都可以考慮歸入domain service。

domain event

領域事件。領域中產生的一些訊息事件,通過事件通知/訂閱的方式,可以在效能和解耦層面得到好處。

repository

倉庫。我們將倉庫的介面定義歸類在domain層,因為他和domain entity聯絡緊密。倉庫使用者和基礎實施的持久化層互動,完成領域對應的增刪改查操作。

倉庫的實際實現根據不同的儲存介質而不同,可以是redis、oracle、mongodb等。

鑑於現在社群服務的儲存介質有三套:oracle、redis、mongodb,且各個儲存介質的欄位屬性名不一致,因此需要使用translator來做翻譯,將持久化層的物件翻譯為統一的領域物件。

translator

翻譯器。將持久化層的物件翻譯為統一的領域物件。

翻譯器中不應當有業務邏輯在裡面,主要負責格式轉換、欄位對映等職責。

infrastructure層

基礎設施層提供公共功能元件,供controller、service、domain層呼叫。

repository impl

對domain層repository介面的實現,對應每種儲存介質有其特定實現,如oracle的mapper,mongodb的dao等等。repository impl會呼叫mybatis、mongo client、redis client完成實際的儲存層操作。

checkLogin

許可權校驗器,判定客戶端是否有訪問該資源的許可權。提供給User Interface層的Controller呼叫。

exception

異常分類及定義,同時提供公共的異常處理邏輯,具體由ExceptionHandler實現。

transport

transport完成和第三方服務的互動,可以有多種協議形式的實現,如http+json、tcp+自定義協議等,配套使用的還有Resolver解析器,用於對第三方服務的請求和響應進行適配,提供一個防腐層(AnticorruptionLayer,DDD原書P255)的作用。

transcation

提供事務管理,交給Spring管理。

logging

日誌模組,記錄trace日誌,使用log4j完成。

message resource

訊息資源管理,交給Spring統一管理。

模組結構

本節介紹ECO系統的模組結構,由於是使用java語言實現,也就是java工程中的包結構,可以直觀地看出框架的落地實現效果。

 各個package的詳細解釋參考上節框架詳述,著重注意如下幾點:

  • factory是專職為model服務的,因此放入對應的entity的modle package中;
  • domain.repository包裡面只有倉庫的介面定義,實際的實現交給了infrastrcture中的repository module,該做法被稱作"依賴倒置",好處在於確保domoain層語義完整,同時對確保業務領域的一致性也有幫助,再者可以在domain實現記憶體形式的repository啞實現,從而讓domain可以真正脫離掉infrastructure;
  • translator module正常情況下不需要,但在重構老系統時,往往需要隔離掉儲存模型對業務模型的影響,使用translator來講儲存模型翻譯為業務模型;
  • infrastructure.repository作為倉庫層,會將實體的增刪改查操作委託給具體的儲存層服務,如oracle對應的mapper實現,mongodb對應dao實現,還有redis的實現;
  • ECO中domain event的生產者和消費者實現均交給infrastructure.rabbitmq package實現。