不做大哥好多年 不做大哥好多年
首页
  • MySQL
  • Redis
  • Elasticsearch
  • Kafka
  • Etcd
  • MongoDB
  • TiDB
  • RabbitMQ
  • 01.Python
  • 02.GO
  • 03.Java
  • 04.业务问题
  • 05.关键技术
  • 06.项目常识
  • 10.计算机基础
  • Docker
  • K8S
  • 容器原理
  • Istio
  • 01.GO基础
  • 02.面向对象
  • 03.并发编程
  • 04.常用库
  • 05.数据库操作
  • 06.Beego框架
  • 07.Beego商城
  • 08.GIN框架
  • 09.GIN论坛
  • 10.微服务
  • 01.Python基础
  • 02.Python模块
  • 03.Django
  • 04.Flask
  • 05.SYL
  • 06.Celery
  • 10.微服务
  • 01.Java基础
  • 02.面向对象
  • 03.Java进阶
  • 04.Web基础
  • 05.Spring框架
  • 100.微服务
  • 数据结构
  • 算法基础
  • 算法题分类
  • 前置知识
  • PyTorch
  • Langchain
  • Linux基础
  • Linux高级
  • Nginx
  • KeepAlive
  • ansible
  • zabbix
  • Shell
  • Linux内核

逍遥子

不做大哥好多年
首页
  • MySQL
  • Redis
  • Elasticsearch
  • Kafka
  • Etcd
  • MongoDB
  • TiDB
  • RabbitMQ
  • 01.Python
  • 02.GO
  • 03.Java
  • 04.业务问题
  • 05.关键技术
  • 06.项目常识
  • 10.计算机基础
  • Docker
  • K8S
  • 容器原理
  • Istio
  • 01.GO基础
  • 02.面向对象
  • 03.并发编程
  • 04.常用库
  • 05.数据库操作
  • 06.Beego框架
  • 07.Beego商城
  • 08.GIN框架
  • 09.GIN论坛
  • 10.微服务
  • 01.Python基础
  • 02.Python模块
  • 03.Django
  • 04.Flask
  • 05.SYL
  • 06.Celery
  • 10.微服务
  • 01.Java基础
  • 02.面向对象
  • 03.Java进阶
  • 04.Web基础
  • 05.Spring框架
  • 100.微服务
  • 数据结构
  • 算法基础
  • 算法题分类
  • 前置知识
  • PyTorch
  • Langchain
  • Linux基础
  • Linux高级
  • Nginx
  • KeepAlive
  • ansible
  • zabbix
  • Shell
  • Linux内核
  • Python

  • GO

  • Java

  • 业务问题

  • 关键技术

  • 项目常识

    • 01.DDD领域驱动设计
      • 01.DDD领域驱动设计
        • 1、DDD概述
        • 2、贫血和充血模型
        • 1) 贫血模型
        • 2) 充血模型
        • 3、DDD项目结构
        • 4、DDD凤城架构图
      • 02.DDD模型举例
        • 0、事例
        • 1、领域事件(Domain Event)
        • 2、聚合(Aggregate)
        • 1、聚合定义
        • 2、domain/demo/agg/aggroot.go
        • 3、实体(Entity)
        • 1、实体定义
        • 2、domain/demo/entity/entity.go
        • 4、值对象(Value Object)
        • 1、实体定义
        • 2、domain/demo/vo/vo.go
        • 5、领域服务(Domain Service)
        • 1、领域服务定义
        • 2、domain/demo/service/svc.go
        • 6、仓储(Repository)
        • 1、仓储定义
        • 2、domain/demo/repo/repo.go
    • 02.Redis_JWT_三方登录
    • 03.Restful风格
    • 04.三方支付
    • 21.后端场景题Part1
    • 22.后端场景题Part2
  • 计算机基础

  • 区块链

  • 常识
  • 项目常识
xiaonaiqiang
2024-12-27
目录

01.DDD领域驱动设计

# 01.DDD领域驱动设计

参考 (opens new window)

# 1、DDD概述

  • 领域驱动设计是一种软件开发方法,它强调专注于业务领域的核心概念和逻辑
  • DDD 的目标是通过更好地理解业务领域来创建高度可维护、灵活和可扩展的软件系统
  • 领域驱动设计的核心思想包括领域模型、限界上下文、实体、值对象、聚合根等概念
  • 领域模型

    • 业务的抽象表示,包括业务规则、实体和流程,帮助团队理解和建模业务领域
    • 例:描述了购物车、订单和支付等业务概念
  • 实体和值对象

    • Order(订单)具有唯一标识是实体
    • Product(商品)没有唯一标识 是值对象
  • 聚合和聚合根

    • Order是一个聚合根,管理订单和其商品项
  • 限界上下文

    • 不同子系统有不同的领域模型,通过限界上下文来 确定某一领域模型的边界
    • 订单管理和支付管理是不同的限界上下文,各自有独立的领域模型
  • 领域服务

    • 处理特定业务逻辑的服务,通常不属于实体或值对象
    • 例: PaymentService(支付服务)负责处理支付逻辑
  • 事件驱动架构

    • 通过事件传递系统中的重要事务,支持松耦合、异步、分布式系统
    • 例:OrderPaid事件触发库存更新、发货等操作

# 2、贫血和充血模型

# 1) 贫血模型

  • 贫血模型中 业务逻辑被分散到外部的服务类中,实体对象仅作为数据容器,没有行为

  • 这样会导致实体的行为和状态变得不一致,无法很好地维护复杂的业务规则

  • 例:

    • Order实体:只存储数据(OrderID, Status, TotalAmount)。
    • OrderService:
      • 包含所有业务逻辑(支付和发货)
      • Order只是数据容器,所有行为被移到OrderService中
  • 贫血模型 - 订单实体

    • // 贫血模型 - 订单实体
      type Order struct {
      	OrderID    string
      	Status     string
      	TotalAmount float64
      }
      
      1
      2
      3
      4
      5
      6
  • OrderService 包含所有业务逻辑(支付和发货)

    • // 贫血模型 - 订单服务
      type OrderService struct{}
      
      func (s *OrderService) PayOrder(order *Order) error {
      	if order.Status != "CREATED" {
      		return errors.New("order cannot be paid")
      	}
      	order.Status = "PAID"
      	fmt.Println("Order Paid:", order.OrderID)
      	return nil
      }
      
      func (s *OrderService) ShipOrder(order *Order) error {
      	if order.Status != "PAID" {
      		return errors.New("order cannot be shipped")
      	}
      	order.Status = "SHIPPED"
      	fmt.Println("Order Shipped:", order.OrderID)
      	return nil
      }
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20

# 2) 充血模型

  • 充血模型中 实体不仅包含数据,还包含与数据相关的业务逻辑

  • 通过将行为封装到实体中,充血模型让领域模型更加完整,并且更加符合DDD的设计原则

  • 例:

    • Order实体:除了包含数据外,Order实体还包含了支付(Pay)和发货(Ship)的方法

    • 所有业务逻辑都在Order实体中,订单的支付和发货由Order本身来管理,而不是分散到外部的服务类中

  • 说明:

    • 订单领域可能需要与支付领域交互,可以通过一个 PaymentService 来处理支付逻辑
    • 支付领域可能通过 事件监听 来响应订单的支付,而不是由订单直接调用支付方法
// 充血模型 - 订单实体
type Order struct {
	OrderID    string
	Status     string
	TotalAmount float64
}

func (o *Order) Pay() error {
	if o.Status != "CREATED" {
		return errors.New("order cannot be paid")
	}
	o.Status = "PAID"
	fmt.Println("Order Paid:", o.OrderID)
	return nil
}

func (o *Order) Ship() error {
	if o.Status != "PAID" {
		return errors.New("order cannot be shipped")
	}
	o.Status = "SHIPPED"
	fmt.Println("Order Shipped:", o.OrderID)
	return nil
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# 3、DDD项目结构

server
├── interfaces     # 第一层:接口层
│   ├── assembler           # 实现 DTO 数据传输对象与 Domain Entity 之间的相互转换和数据交换
│   ├── controller          # 控制器路由函数
│   └── dto/vo              # 可包含多个领域对象的属性:DTO(Data Transfer Object)主要关注数据的传输
                            
├── application    # 第二层:业务调度层
│   ├── event               # 微服务事件推送或订阅
│   │   ├── publish         # 事件发布
│   │   └── subscribe       # 事件订阅
│   └── service             # 用于连接 Controller 和 Domain,进行三方接口调用等其他操作

├── domain          # 第三层:领域服务层(领域逻辑和领域对象,主要的业务逻辑,采用充血模型)
│   ├── aggregate01         # Aggregate 聚合根目录
│   │   ├── entity          # entity 实体、VO 值对象以及工厂模式(Factory)相关
│   │   ├── event           # 事件发布和订阅
│   │   ├── repository      # 仓储:持久化领域对象
│   │   └── service         # 领域服务代码
│   ├── aggregate02
│   └── ...

├── infrastructu   # 第四层:基础设施层
│   ├── api                 # 第三方 API/SDK
│   ├── configs             # 配置参数变量
│   ├── database            # 初始化数据库
│   ├── mq                  # 消息队列连接和配置
│   ├── persistence         # 数据持久化(Domain 层 repository 的具体实现,数据库 CRUD 操作) 
│   └── pkg                 # 工具函数
│       ├── common          # 与业务相关包
│       └── utils           # 公共基础包

└── main.go                 # 主入口
├── go.mod
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

# 4、DDD凤城架构图

# 02.DDD模型举例

# 0、事例

├── domain  // 领域层
│   └── demo
│       ├── agg  // 聚合
│       │   ├── agg1.go
│       │   ├── aggroot.go
│       │   └── readme.md
│       ├── entity  // 实体
│       │   ├── entity.go
│       │   └── readme.md
│       ├── repo  // 仓储
│       │   ├── readme.md
│       │   └── repo.go
│       ├── service  // 领域服务
│       │   ├── readme.md
│       │   └── svc.go
│       └── vo  // 值对象
│           ├── readme.md
│           └── vo.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 1、领域事件(Domain Event)

  • 领域事件是领域驱动设计中的一个重要概念,它用于表示系统中发生的重要业务变化或状态转换
  • 领域事件是对业务领域中某一事实或情境的描述,它通常是由业务专家或领域内的相关人员定义的,而不是由技术团队来确定
  • 订单创建事件

    • 描述:当客户成功下单时,系统会发布一个“订单创建”事件。

    • 示例:OrderCreatedEvent,包含订单号、客户信息、商品列表等关键信息。

  • 库存不足事件

    • 描述:当系统检测到某个商品库存不足时,触发“库存不足”事件。

    • 示例:InventoryInsufficientEvent,包含商品ID、当前库存数量、需要的最小库存量等信息。

# 2、聚合(Aggregate)

# 1、聚合定义

  • 此目录下存放聚合(Aggregate)与聚合根(Aggregate Root)
  • 聚合根
    • 聚合根通常是一个包含业务逻辑和数据的结构体或接口
    • 它代表了整个聚合的入口点,并负责维护聚合内部对象之间的一致性
    • 在该项目实践中,我们将聚合根定义为一个接口,该接口包含了聚合的所有业务方法,以及聚合内部对象的访问方法
  • 聚合
    • 聚合是一组相关的对象的集合,它们一起执行一个特定的业务功能
    • 聚合根是聚合的入口点,通过聚合根可以访问聚合内部的所有对象
    • 在该项目实践中,我们将聚合定义为一个结构体,该结构体包含了聚合内部的所有对象,以及聚合内部对象的访问方法

# 2、domain/demo/agg/aggroot.go

package agg

import (
	"rms/domain/demo/entity"
	"rms/domain/demo/repo"
	"rms/domain/demo/vo"
)

// BookAggregateRoot 书籍聚合根
type BookAggregateRoot struct {
	Book       *entity.Book
	Inventory  *vo.InventoryValueObject
	Repository repo.BookRepository
}

// OrderAggregateRoot 订单聚合根
type OrderAggregateRoot struct {
	Order      *entity.Order
	Repository repo.OrderRepository
}

// GetBook 获取书籍
func (bar *BookAggregateRoot) GetBook() *entity.Book {
	return bar.Book
}

// GetInventory 获取库存
func (bar *BookAggregateRoot) GetInventory() *vo.InventoryValueObject {
	return bar.Inventory
}

func (bar *BookAggregateRoot) GetTest() string {
	return "arr root test"
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

# 3、实体(Entity)

# 1、实体定义

  • 实体是具有唯一标识的对象,它们的标识是通过唯一标识符来实现的。
  • 实体的状态可能随着时间的推移而改变,但是实体的标识是不变的。
  • 在该项目实践中,我们将实体定义为一个结构体,该结构体包含了实体的所有属性,以及实体的所有方法
  • 例子:
    • 以图书为例,每本书都有唯一的标识(如ISBN),即使两本书的其他属性相同,它们仍然是不同的实体。

# 2、domain/demo/entity/entity.go

package entity

import "time"

type EntityA struct {
}

func (e *EntityA) NewA() *EntityA {
	return &EntityA{}
}

func (e *EntityA) GetTest() string {
	return "entity a test"
}

type Book struct {
	ID     int
	Title  string
	Author string
	Price  float64
}

// OrderItem 表示订单中的一项书籍
type OrderItem struct {
	BookID   int
	Quantity int
}

// Order 实体
type Order struct {
	ID        int
	UserID    int
	OrderDate time.Time
	Items     []OrderItem
	Total     float64
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

# 4、值对象(Value Object)

# 1、实体定义

  • 值对象是没有唯一标识的对象,其相等性是根据其所有属性的值来判断的

  • 即,如果两个值对象的所有属性值都相等,那么这两个值对象就是相等的

  • 值对象通常是不可变的,一旦创建就不能修改,如果需要修改值对象的某些属性,应该创建一个新的值对象

  • 例子:

    • 以货币金额为例,一个表示货币金额的值对象可以包含两个属性,金额数值和货币类型

    • 两个值对象如果金额和货币类型相同,就被认为是相等的

    • type Money struct {
          Amount   float64
          Currency string
      }
      
      1
      2
      3
      4

# 2、domain/demo/vo/vo.go

package vo

type InventoryValueObject struct {
	BookID  int
	InStock int
}
1
2
3
4
5
6

# 5、领域服务(Domain Service)

# 1、领域服务定义

  • 特点:
    • 领域服务是一种协作的概念,它不是实体或值对象,而是执行某个特定领域操作或任务的服务。
    • 领域服务通常涉及多个实体和值对象,用于执行业务规则和操作,而不属于任何单一的实体或值对象。
  • 例子:
    • 在一个电子商务系统中,计算订单总金额的过程可能涉及多个商品(实体)和它们的数量以及价格(值对象)
    • 为了执行这个计算,你可能会创建一个领域服务,如OrderCalculationService

# 2、domain/demo/service/svc.go

  • 这个例子中,OrderService 是一个领域服务,用于保存订单和计算订单价格。
package service

import (
	"time"

	"rms/domain/demo/entity"
	"rms/domain/demo/repo"
)

// OrderService 订单领域服务
type OrderService struct {
	BookRepo  repo.BookRepository
	OrderRepo repo.OrderRepository
}

// PlaceOrder 是一个领域服务方法,用于创建并保存订单
func (s *OrderService) PlaceOrder(userID int, items []entity.OrderItem) (*entity.Order, error) {
	// 创建订单实例
	order := &entity.Order{
		UserID:    userID,
		OrderDate: time.Now(),
		Items:     items,
		// 计算订单总价
		Total: calculateTotalPrice(items, s.BookRepo),
	}

	// 保存订单到仓储
	err := s.OrderRepo.Save(order)
	if err != nil {
		return nil, err
	}

	return order, nil
}

// 计算订单总价
func calculateTotalPrice(items []entity.OrderItem, bookRepo repo.BookRepository) float64 {
	var total float64
	for _, item := range items {
		// 查询书籍价格并累加
		book, err := bookRepo.FindByID(item.BookID)
		if err != nil {
			// 处理错误
		}
		total += book.Price * float64(item.Quantity)
	}
	return total
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48

# 6、仓储(Repository)

# 1、仓储定义

  • 仓储是一种模式或接口,用于封装对领域对象的持久化和检索操作
  • 仓储的主要责任是将领域对象持久化到数据存储(如数据库、文件系统等)中
  • 它提供了一种抽象的方式来访问领域对象,而不暴露底层的数据存储细节
  • 从数据存储中检索领域对象,并将其重新构造为领域对象的实例

# 2、domain/demo/repo/repo.go

package repo

import "rms/domain/demo/entity"

type BookRepository interface {
	FindByID(id int) (*Book, error)
	Save(book *Book) error
}

type Book struct{
    ID   int
    Name string
    // 其他属性...
}

func (BookRepo) FindByID(id int) (*Book, error) {
	//TODO implement me
	panic("implement me")
}

func (Book) Save(book *Book) error {
	//TODO implement me
	panic("implement me")
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
上次更新: 2025/2/19 16:42:39
05.连接池设计
02.Redis_JWT_三方登录

← 05.连接池设计 02.Redis_JWT_三方登录→

最近更新
01
300.整体设计
06-10
02
06.LangGraph
06-09
03
202.AI销售智能体
06-07
更多文章>
Theme by Vdoing | Copyright © 2019-2025 逍遥子 技术博客 京ICP备2021005373号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式