成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

架構師必備 - DDD之落地實踐

開發 項目管理
戰略設計、戰術設計、核心域、支撐域、值對象、實體、聚合... 對我們實際落地卻沒有太多的幫助,下面介紹下我在SpringBoot中應用DDD的落地方案。

哈嘍,大家好,我是指北君。

?今天帶大家認識下DDD,一個聽起來很垃圾卻真的很牛X的設計思想,架構師必備!?

前言

在日常工作中,接手或維護的工程,大多數使用的是三層架構,即controller、service、dao三層,在使用的過程中,會遇到很多問題:

  • 面向數據建模,面向過程編程,沒有真正“面向對象”
  • 只注重結果,不注重過程,service層動輒數百上千行,充斥著過程代碼、膠水代碼,要么臃腫、要么流水賬、要不重復、要么邏輯分散,后期極難維護
  • 代碼耦合嚴重,層與層之間互相調用、逆向調用,牽一發而動全身
  • 代碼無法體現業務,在大家都不愛寫注釋的情況下,隨著時間的推移,代碼業務邏輯將無人理解,不敢改也改不動。

那么有沒有一個好的解決方案呢?今天要講的DDD就是一個不錯的選擇。

DDD

DDD,即領域驅動設計,完美的解決了以上問題:

  • 面向領域建模,面向對象編程,代碼直接映射現實世界概念,貼近業務,離客戶更近
  • 領域邏輯高內聚,符合Java開發原則
  • 技術細節變更如數據庫、緩存、定時器等的變更對業務邏輯影響比較小,非常適合插件式架構
  • 代碼可讀性、可維護性更強,對后續擴展、移植等支持更好,分層更加科學

DDD的概念,在網上很容易找到,這里就不贅述了。

然而網上DDD的文章雖然很多,但大多數是理論知識,介紹的無非就是一些名詞:戰略設計、戰術設計、核心域、支撐域、值對象、實體、聚合...  對我們實際落地卻沒有太多的幫助,下面介紹下我在SpringBoot中應用DDD的落地方案。

落地方案

1、代碼分層

圖片

代碼分層

  • 用戶接口層:圖中的api包(即controller層,我嫌controller后綴太長...)
  • 應用層:這里使用了命令模式,并且讀寫分離成了兩個包(command、query),如果不使用命令模式可以合并成一個service包
  • 領域層:domain包,使用JPA(對DDD有良好的支持)
  • 基礎設施層:infra包,其他所有的公用組件都放在這里,如果使用DIP依賴倒置,那么實現類也放在這里。
  • model模型:model包,用于存放不同層間傳遞的對象,這些對象我試過放到好些地方,最后發現還是提出來統一放在一個包下比較好(便于服務間調用時共用對象)

2、層級關系及模型傳遞

圖片

分層及調用關系

3、分層詳細說明

  • api包(controller)
@Tag(name = "用戶", description = "用戶")
@RestController
@RequestMapping(value = "/api/sys-user")
public class SysUserApi extends BaseApi {


@ApiResult
@Operation(summary = "根據ID查詢用戶")
@GetMapping("/{id}")
public SysUserVo get(@PathVariable Long id) {
return queryExecutor.execute(new SysUserByIdQry(id));
}


@Pagination(total = true)
@ApiResult
@Operation(summary = "分頁查詢用戶")
@GetMapping
public List<SysUserVo> getList(SysUserQo sysUserQo) {
return queryExecutor.execute(new SysUserListQry(sysUserQo));
}


@ApiResult
@Operation(summary = "新增用戶")
@PostMapping
public void save(@Valid @RequestBody SysUserDto sysUserDto) {
commandExecutor.execute(new SysUserCommonCmd(sysUserDto));
}
}

在BaseApi中封裝了兩個命令執行類queryExecutor和commandExecutor,調用應用層時執行不同的命令即可,無需@Autowired引入不同的服務

@ApiResult加上這個自定義注解后,對返回結果統一封裝

@Pagination加上這個自定義注解后,會自動將分頁參數存入線程變量,后面查詢時也會自動獲取分頁參數,返回結果統一封裝時也會加上分頁信息

Qo是查詢參數對象,Dto是增刪改等命令參數對象,返回對象為Vo,這里要注意,Entity絕對不能暴露到這一層,需要轉換為Vo再返回

在這一層中,每個方法幾乎就是一行執行命令的語句,一般情況不進行業務邏輯(當然也有特殊情況咯)

  • command包
@AllArgsConstructor
public class SysDeptAddCmd implements Command<Void> {


private SysDeptDto sysDeptDto;


@Override
public Void execute(Executor executor) {
// 獲取命令的接收者:領域服務
SysDeptManager receiver = executor.getReceiver(SysDeptManager.class);
// 對象模型轉換,由DTO轉為Entity,使用了MapStruct
SysDept sysDept = SysDeptMapper.INSTANCE.toSysDept(sysDeptDto);
// 使用JPA保存
receiver.save(sysDept);
return null;
}
}

增刪改命令,很薄的一層,作為一項工作的組織者,幾乎沒有業務邏輯,調用領域服務和充血對象方法

命令模式,實現自定義Command接口,泛型為返回值

通過屬性和構造方法(使用lombok注解)接收參數

一個命令里只有一個execute方法,缺點是會產生大量的命令類,一個類相當于之前service類中的一個方法,但是這樣符合了單一職責原則

通過executor.getRecerver方法獲取到領域服務(manager)

DTO絕對不下探到領域層中,需要先由DTO轉換為Entity(轉換方法這里使用的MapStruct,以后再單獨細講)

  • query包
@AllArgsConstructor
public class SysDeptByIdQry extends CommonQry<SysDeptVo> {


private Long id;


@Override
public SysDeptVo execute(Executor executor) {
if (id == null) {
throw new BusinessException("部門ID不能為空");
}
QSysDept sysDept = QSysDept.sysDept;
return queryFactory.select(this.fields())
.from(sysDept)
.where(sysDept.deleted.eq(false), sysDept.id.eq(id))
.fetchOne();
}


/**
* 部門VO映射
*
* @return QBean<SysDeptVo>
*/
public static QBean<SysDeptVo> fields() {
QSysDept sysDept = QSysDept.sysDept;
return Projections.fields(
SysDeptVo.class,
sysDept.deptName,
sysDept.orderNum,
sysDept.id
);
}
}

命令模式,繼承自定義CommonQry基類(此類也實現了自定義的Command接口,其中引用了QueryDSL的queryFactory類,且封裝了分頁方法),泛型為返回值

query包中的查詢命令與command包中的命令大體相同,唯一區別是query命令理論上直接查詢數據庫,不調用領域層

由于JPA對于復雜查詢不太好用,這里強烈推薦使用QueryDSL(以后再單獨細講),圖中是一個簡單的使用例子

  • domain包

類較多,代碼部分不一一羅列:

由Entity類自動生成數據庫表,僅維護Entity類(屏蔽數據庫)

設計Entity時根據實際業務靈活使用@OneToMany、@OneToOne等注解(聚合根的概念)

聚合根不要太大,80%的情況一個聚合根中只包含一個實體(不要過度設計成大聚合根)

不要使用貧血模型,而是要面向對象,屬于對象的方法要放到對象中,但是對象中不建議引入倉庫repository類,需要操作數據庫的方法寫在領域服務manager里

業務邏輯盡量寫在領域服務(manager)中,不斷提取、抽象不同的方法供應用層調用

適當的使用領域事件,JPA可以在Entity中使用@DomainEvents注解來發送領域事件

心得

通過DDD對業務理解更加透徹,寫的代碼可以更好的傳達客戶的業務訴求

能夠盡情的編寫低耦合的、符合單一職責、開閉等原則、封裝、繼承、多態的代碼,是很身心愉悅的

前期相比傳統架構代碼量更多,開發人員前期投入更多:

  • 領域的合理劃分、實體的合理設計
  • 大量的DTO、VO等數據對象
  • 大量的數據對象轉換方法
  • 大量的命令類
  • ...

但是,除非是特別簡單的功能,對于一個中等復雜的系統,這些前期的付出還是值得的,一張圖說明:

圖片

小結

以上簡單介紹了下我對DDD的理解和實踐,并通過實際的代碼展現了如何在SpringBoot中應用DDD,希望能為大家提供一個思路。

責任編輯:武曉燕 來源: Java技術指北
相關推薦

2023-08-28 07:28:41

項目領域層充血模型

2022-05-23 09:20:00

數據庫架構

2022-10-08 09:18:19

架構模型

2015-06-10 11:22:41

云計算云架構師

2022-05-27 15:19:38

架構師溝通認知

2019-10-30 16:24:34

分層架構緩存

2023-12-01 07:24:40

軟件架構

2023-08-06 23:31:36

架構系統RPC

2023-09-27 10:23:19

NoSQL數據模型

2021-10-09 09:52:49

MYSQL開發數據庫

2012-08-13 16:48:31

架構師

2021-10-22 08:00:00

架構開發技術

2019-07-16 13:59:43

數據庫MySQL軟件

2011-10-31 09:22:07

系統架構

2019-07-29 11:25:23

架構師架構方案架構

2021-11-18 13:14:08

DDD聚合代碼

2021-04-27 09:35:36

業務領域建模

2011-11-01 09:02:26

系統架構師

2011-11-02 09:01:30

系統架構師

2011-10-18 09:25:04

系統架構師
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 国产精品一区二区无线 | 玩丰满女领导对白露脸hd | 少妇一区二区三区 | 国产精品 欧美精品 | 久久精品毛片 | 午夜视频在线 | 欧美一区二区成人 | 国产a视频| 国产精品久久久久久久久 | 国产伦精品一区二区三区四区视频 | 一区二区视频在线 | 99免费| 国产精品一区二区久久 | 亚洲国产精品视频 | 久久精品性视频 | 精品国产精品三级精品av网址 | 亚洲欧美在线一区 | 久久99精品国产 | 91精品国产91久久久久久 | 日本欧美大片 | 91国内外精品自在线播放 | 欧美日韩一区二区三区视频 | 成人中文网 | 狠狠撸在线视频 | 久久久久久综合 | 亚洲欧美日韩在线 | 亚洲三区视频 | 毛片大全 | 欧美性一级 | 日韩欧美在线视频 | www.com久久久| 在线看av的网址 | 国产精品久久久久久久久久 | 国产激情精品 | 一二三四在线视频观看社区 | 四虎影院一区二区 | 国外成人免费视频 | 国产精品永久免费观看 | 九九免费视频 | 久久久久一区 | 久久久久久久久综合 |