当前位置: 首页 > news >正文

领域驱动设计(DDD)

领域驱动设计

在软件所属领域之后对软件进行结构化和建模的一种方法,首先考虑编写软件的领域,软件的编写应该反映该领域
领域:软件处理的主题或问题
DDD主张工程团队必须与主题专家(SME)交谈,将知识反映在软件中

实体

创建entity,存放实体
实体:一个结构体包含标志符,状态可能会改变(实体的值可以改变)
在这里插入图片描述

// entity包保存所有子领域共享的所有实体
// 一个结构体具有唯一标识符来引用,状态可变package entityimport "github.com/google/uuid"// 实体
// Item表示所有子领域的Item
// bson表示存储在mongodb中的结构体type Item struct {ID          uuid.UUID `json:"id" bson:"id"`Name        string    `json:"name" bson:"name"`Description string    `json:"description" bson:"description"`
}
package entityimport "github.com/google/uuid"// 实体
// Person 在所有领域中代表人
type Person struct {// ID是实体的标识符,该ID为所有子领域共享ID uuid.UUID `json:"id" bson:"id"`//Name就是人的名字Name string `json:"name" bson:"name"`// 人的年龄Age int `json:"age" name:"age"`
}

值对象

结构体不可变,不需要唯一标识符
结构体在创建时没有标识符和持久化值
值对象通常位于领域内,用于描述该领域的某些方面
在这里插入图片描述

package valueobjectimport ("github.com/google/uuid""time"
)// 值对象结构体不可变,不需要唯一标识符和持久化值
// 持久化值:没有唯一标识,但本身具有完整意义的对象
// 相等值通过值判断而不是身份
// 只要Transaction的字段值是都相同的,那么就认为两个Transaction是相等的,不依赖唯一id
// 即时存储在数据库中,只需保存这些字段值,不需要通过id识别// Transaction 表示双方用于支付
// 一旦事务被执行,它就不能改变状态
type Transaction struct {Amount    int       `json:"amount" bson:"amount"`From      uuid.UUID `json:"from" bson:"from"`To        uuid.UUID `json:"to" bson:"to"`CreatedAt time.Time `json:"createdAt" bson:"createdAt"`
}

聚合

一组实体和值对象的组合
聚合时领域概念
聚合的目的:
业务逻辑将应用于聚合,而不是每个持有该逻辑的实体
聚合不允许直接访问底层实体
聚合中应该只有一个实体作为根实体,意味着根实体的引用也用于聚合
在这里插入图片描述

package aggregateimport ("errors""github.com/google/uuid""test/DDD/entity""test/DDD/valueobject"
)// 聚合
// 一组实体和值对象的组合,用来表示一个业务实体
// 只有一个实体作为根实体
// 对于一个聚合来说实体的id是唯一标识符var (// 当person在newcustom工厂中无效时返回ErrInvalidPersonErrInvalidPerson = errors.New("a customer has to have an valid person")
)// 因为要实现聚合可序列化,所以会选择跳过不能访问底层实体的规则
// Customer 聚合包含了代表一个客户所需的所有实体
// 将实体设为指针,是因为实体可以改变状态
// 想反映在运行时所有访问它的实例中
// 保存为非指针不能改变状态
type Customer struct {// Person是客户的根实体// person.ID是聚合的主标识符Person *entity.Person `bson:"person"`//一个客户可以持有许多产品Products []*entity.Item `bson:"products"`// 一个客户可以执行许多事务Transactions []valueobject.Transaction `bson:"transactions"`
}

工厂函数

工厂模式是一种设计模式,在创建所需实例的函数中封装复杂逻辑,调用者不需要知道其中细节
DDD建议使用工厂创建复杂的聚合,仓库和服务
(在实际应用中,会在领域中包含聚合的Customer和工厂)

// NewCustomer 是创建新的Customer聚合的工厂
// 它将验证名称是否为空
func NewCustomer(name string) (Customer, error) {// 验证Name不是空的if name == "" {return Customer{}, ErrInvalidPerson}// 创建一个新person并生成IDperson := &entity.Person{Name: name,ID:   uuid.New(),}// 创建一个customer对象并初始化所有的值以避免空指针异常return Customer{Person:       person,Products:     make([]*entity.Item, 0),Transactions: make([]valueobject.Transaction, 0),}, nil
}

开启单元测试

package aggregate_testimport ("test/DDD/aggregate""testing"
)func TestCustomer_NewCustomer(t *testing.T) {// 构建我们需要的测试用例数据结构type testCase struct {test        stringname        stringexpectedErr error}//创建新的测试用例testCases := []testCase{{test:        "Empty Name validation",name:        "",expectedErr: aggregate.ErrInvalidPerson,}, {test:        "Valid Name",name:        "Percy Bolmer",expectedErr: nil,},}for _, tc := range testCases {// Run Testst.Run(tc.test, func(t *testing.T) {//创建新的customer_, err := aggregate.NewCustomer(tc.name)//检查错误是否与预期的错误匹配if err != tc.expectedErr {t.Errorf("Expected error %v, got %v", tc.expectedErr, err)}})}}

相关文章:

  • 企业微信授权登录(uniapp项目)
  • Fullstack 面试复习笔记:HTML / CSS 基础梳理
  • Java编程之原型模式
  • 35 C 语言字符串转数值函数详解:strtof、strtod、strtold(含 errno 处理、ERANGE 错误)
  • CMS、G1、ZGC、Shenandoah 的全面对比
  • 变幻莫测:CoreData 中 Transformable 类型面面俱到(一)
  • Android 视图系统入门指南
  • OkHttp 3.0源码解析:从设计理念到核心实现
  • AI整合SEO关键词智能策略
  • 阿里云 RDS mysql 5.7 怎么 添加白名单 并链接数据库
  • selenium自动化测试学习心得1
  • C++ 信息学奥赛总复习题答案解析(第一章)
  • 在Ubuntu22.04 系统中安装Docker详细教程
  • 【hadoop】相关集群开启命令
  • Vue3学习(4)- computed的使用
  • 基于Python学习《Head First设计模式》第八章 模板方法模式
  • 力扣HOT100之二分查找:33. 搜索旋转排序数组
  • 热电厂中控室无线集控:高清视频监测+PLC远程操控
  • 调试器基本原理
  • 高精度算法--四则运算模板(附例题)
  • 网站的下载链接怎么做/怎样做网站的优化、排名
  • 门户网站模板图片/济南百度竞价开户
  • 宝安做网站的/网站优化费用报价明细
  • b2b免费发布网站大全排名/最近爆发什么病毒感染
  • 建设网站总经理讲话范本/谷歌站长平台
  • 门户网站建设统计表/广州推广引流公司