399 lines
		
	
	
		
			9.8 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			399 lines
		
	
	
		
			9.8 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
| # Domain 域概念详解
 | ||
| 
 | ||
| ## 🤔 什么是 Domain(域)?
 | ||
| 
 | ||
| **Domain(域)** 就像现实世界中的**部门**或**业务范围**。
 | ||
| 
 | ||
| ### 📚 现实世界的类比
 | ||
| 
 | ||
| 想象一个大公司:
 | ||
| 
 | ||
| ```
 | ||
| 阿里巴巴集团
 | ||
| ├── 电商域 (淘宝、天猫)
 | ||
| ├── 支付域 (支付宝)
 | ||
| ├── 云计算域 (阿里云)
 | ||
| ├── 物流域 (菜鸟)
 | ||
| └── 金融域 (蚂蚁金服)
 | ||
| ```
 | ||
| 
 | ||
| 每个域都有:
 | ||
| 
 | ||
| -   **专门的团队** - 不同的开发团队
 | ||
| -   **独立的业务** - 各自负责不同的业务功能
 | ||
| -   **清晰的边界** - 知道自己管什么,不管什么
 | ||
| -   **独立运作** - 可以独立决策和发展
 | ||
| 
 | ||
| ## 🏗️ 在软件架构中的应用
 | ||
| 
 | ||
| ### 传统方式 vs 域驱动方式
 | ||
| 
 | ||
| #### ❌ 传统方式(技术驱动)
 | ||
| 
 | ||
| ```
 | ||
| 项目结构:
 | ||
| ├── controllers/     # 所有控制器
 | ||
| ├── services/       # 所有服务
 | ||
| ├── models/         # 所有数据模型
 | ||
| └── utils/          # 工具类
 | ||
| 
 | ||
| 问题:
 | ||
| - 用户相关、产品相关、支付相关的代码混在一起
 | ||
| - 不同业务逻辑耦合
 | ||
| - 团队协作困难
 | ||
| - 修改一个功能可能影响其他功能
 | ||
| ```
 | ||
| 
 | ||
| #### ✅ 域驱动方式(业务驱动)
 | ||
| 
 | ||
| ```
 | ||
| 项目结构:
 | ||
| ├── user-domain/        # 用户域
 | ||
| │   ├── user/          # 用户管理
 | ||
| │   ├── auth/          # 认证授权
 | ||
| │   └── profile/       # 用户资料
 | ||
| ├── product-domain/     # 产品域
 | ||
| │   ├── catalog/       # 产品目录
 | ||
| │   ├── inventory/     # 库存管理
 | ||
| │   └── pricing/       # 价格管理
 | ||
| ├── payment-domain/     # 支付域
 | ||
| │   ├── wallet/        # 钱包
 | ||
| │   ├── order/         # 订单
 | ||
| │   └── billing/       # 计费
 | ||
| └── notification-domain/ # 通知域
 | ||
|     ├── email/         # 邮件通知
 | ||
|     ├── sms/           # 短信通知
 | ||
|     └── push/          # 推送通知
 | ||
| 
 | ||
| 优势:
 | ||
| - 按业务功能组织代码
 | ||
| - 团队可以独立开发不同的域
 | ||
| - 修改用户功能不会影响支付功能
 | ||
| - 新人容易理解业务边界
 | ||
| ```
 | ||
| 
 | ||
| ## 🎯 你的项目中的 Domain 重构
 | ||
| 
 | ||
| ### 当前问题
 | ||
| 
 | ||
| 你的项目中有这些编码式命名:
 | ||
| 
 | ||
| ```
 | ||
| IVYZ - 不知道是什么业务
 | ||
| FLXG - 不知道是什么业务
 | ||
| QYGL - 不知道是什么业务
 | ||
| YYSY - 不知道是什么业务
 | ||
| JRZQ - 不知道是什么业务
 | ||
| ```
 | ||
| 
 | ||
| ### 重构后的 Domain 设计
 | ||
| 
 | ||
| ```
 | ||
| 用户域 (User Domain)
 | ||
| ├── 用户注册登录
 | ||
| ├── 用户信息管理
 | ||
| ├── 企业认证
 | ||
| └── 权限管理
 | ||
| 
 | ||
| 金融域 (Financial Domain)
 | ||
| ├── 钱包管理
 | ||
| ├── 支付处理
 | ||
| ├── 账单生成
 | ||
| └── 财务报表
 | ||
| 
 | ||
| 数据服务域 (Data Service Domain)
 | ||
| ├── 风险评估服务 (原 FLXG)
 | ||
| ├── 征信查询服务 (原 JRZQ)
 | ||
| ├── 企业信息服务 (原 QYGL)
 | ||
| ├── 数据查询服务 (原 IVYZ)
 | ||
| └── 应用系统服务 (原 YYSY)
 | ||
| 
 | ||
| 产品域 (Product Domain)
 | ||
| ├── 产品目录管理
 | ||
| ├── 产品订阅
 | ||
| ├── 访问控制
 | ||
| └── 白名单管理
 | ||
| 
 | ||
| 平台域 (Platform Domain)
 | ||
| ├── 管理后台
 | ||
| ├── 系统监控
 | ||
| ├── 日志管理
 | ||
| └── 配置管理
 | ||
| ```
 | ||
| 
 | ||
| ## 💻 代码层面的 Domain 实现
 | ||
| 
 | ||
| ### 1. Domain 层的职责
 | ||
| 
 | ||
| ```go
 | ||
| // domain/user/entity/user.go
 | ||
| package entity
 | ||
| 
 | ||
| import (
 | ||
|     "errors"
 | ||
|     "time"
 | ||
| )
 | ||
| 
 | ||
| // User 用户实体
 | ||
| type User struct {
 | ||
|     ID        int64     `json:"id"`
 | ||
|     Username  string    `json:"username"`
 | ||
|     Email     string    `json:"email"`
 | ||
|     Phone     string    `json:"phone"`
 | ||
|     Status    UserStatus `json:"status"`
 | ||
|     CreatedAt time.Time `json:"created_at"`
 | ||
|     UpdatedAt time.Time `json:"updated_at"`
 | ||
| }
 | ||
| 
 | ||
| type UserStatus int
 | ||
| 
 | ||
| const (
 | ||
|     UserStatusActive   UserStatus = 1
 | ||
|     UserStatusInactive UserStatus = 2
 | ||
|     UserStatusBanned   UserStatus = 3
 | ||
| )
 | ||
| 
 | ||
| // 业务规则:用户名必须是3-20个字符
 | ||
| func (u *User) ValidateUsername() error {
 | ||
|     if len(u.Username) < 3 || len(u.Username) > 20 {
 | ||
|         return errors.New("用户名必须是3-20个字符")
 | ||
|     }
 | ||
|     return nil
 | ||
| }
 | ||
| 
 | ||
| // 业务规则:检查用户是否可以登录
 | ||
| func (u *User) CanLogin() bool {
 | ||
|     return u.Status == UserStatusActive
 | ||
| }
 | ||
| 
 | ||
| // 业务规则:激活用户
 | ||
| func (u *User) Activate() error {
 | ||
|     if u.Status == UserStatusBanned {
 | ||
|         return errors.New("被封禁的用户不能激活")
 | ||
|     }
 | ||
|     u.Status = UserStatusActive
 | ||
|     u.UpdatedAt = time.Now()
 | ||
|     return nil
 | ||
| }
 | ||
| ```
 | ||
| 
 | ||
| ### 2. Domain Service (领域服务)
 | ||
| 
 | ||
| ```go
 | ||
| // domain/user/service/user_service.go
 | ||
| package service
 | ||
| 
 | ||
| import (
 | ||
|     "context"
 | ||
|     "errors"
 | ||
| 
 | ||
|     "your-project/domain/user/entity"
 | ||
|     "your-project/domain/user/repository"
 | ||
| )
 | ||
| 
 | ||
| // UserDomainService 用户领域服务
 | ||
| type UserDomainService struct {
 | ||
|     userRepo repository.UserRepository
 | ||
| }
 | ||
| 
 | ||
| func NewUserDomainService(userRepo repository.UserRepository) *UserDomainService {
 | ||
|     return &UserDomainService{
 | ||
|         userRepo: userRepo,
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| // 业务规则:检查用户名是否唯一
 | ||
| func (s *UserDomainService) IsUsernameUnique(ctx context.Context, username string) (bool, error) {
 | ||
|     existingUser, err := s.userRepo.FindByUsername(ctx, username)
 | ||
|     if err != nil {
 | ||
|         return false, err
 | ||
|     }
 | ||
|     return existingUser == nil, nil
 | ||
| }
 | ||
| 
 | ||
| // 业务规则:用户注册
 | ||
| func (s *UserDomainService) RegisterUser(ctx context.Context, username, email, phone string) (*entity.User, error) {
 | ||
|     // 1. 检查用户名唯一性
 | ||
|     unique, err := s.IsUsernameUnique(ctx, username)
 | ||
|     if err != nil {
 | ||
|         return nil, err
 | ||
|     }
 | ||
|     if !unique {
 | ||
|         return nil, errors.New("用户名已存在")
 | ||
|     }
 | ||
| 
 | ||
|     // 2. 创建用户实体
 | ||
|     user := &entity.User{
 | ||
|         Username: username,
 | ||
|         Email:    email,
 | ||
|         Phone:    phone,
 | ||
|         Status:   entity.UserStatusActive,
 | ||
|     }
 | ||
| 
 | ||
|     // 3. 验证业务规则
 | ||
|     if err := user.ValidateUsername(); err != nil {
 | ||
|         return nil, err
 | ||
|     }
 | ||
| 
 | ||
|     // 4. 保存用户
 | ||
|     return s.userRepo.Save(ctx, user)
 | ||
| }
 | ||
| ```
 | ||
| 
 | ||
| ### 3. Repository 接口 (仓储模式)
 | ||
| 
 | ||
| ```go
 | ||
| // domain/user/repository/user_repository.go
 | ||
| package repository
 | ||
| 
 | ||
| import (
 | ||
|     "context"
 | ||
|     "your-project/domain/user/entity"
 | ||
| )
 | ||
| 
 | ||
| // UserRepository 用户仓储接口
 | ||
| type UserRepository interface {
 | ||
|     Save(ctx context.Context, user *entity.User) (*entity.User, error)
 | ||
|     FindByID(ctx context.Context, id int64) (*entity.User, error)
 | ||
|     FindByUsername(ctx context.Context, username string) (*entity.User, error)
 | ||
|     FindByEmail(ctx context.Context, email string) (*entity.User, error)
 | ||
|     Update(ctx context.Context, user *entity.User) error
 | ||
|     Delete(ctx context.Context, id int64) error
 | ||
|     List(ctx context.Context, offset, limit int) ([]*entity.User, error)
 | ||
| }
 | ||
| ```
 | ||
| 
 | ||
| ### 4. 应用层 (Application Layer)
 | ||
| 
 | ||
| ```go
 | ||
| // application/user/service/user_app_service.go
 | ||
| package service
 | ||
| 
 | ||
| import (
 | ||
|     "context"
 | ||
| 
 | ||
|     userDomain "your-project/domain/user/service"
 | ||
|     "your-project/application/user/dto"
 | ||
| )
 | ||
| 
 | ||
| // UserAppService 用户应用服务
 | ||
| type UserAppService struct {
 | ||
|     userDomainService *userDomain.UserDomainService
 | ||
| }
 | ||
| 
 | ||
| func NewUserAppService(userDomainService *userDomain.UserDomainService) *UserAppService {
 | ||
|     return &UserAppService{
 | ||
|         userDomainService: userDomainService,
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| // 用户注册用例
 | ||
| func (s *UserAppService) RegisterUser(ctx context.Context, req *dto.RegisterUserRequest) (*dto.UserResponse, error) {
 | ||
|     // 1. 调用领域服务
 | ||
|     user, err := s.userDomainService.RegisterUser(ctx, req.Username, req.Email, req.Phone)
 | ||
|     if err != nil {
 | ||
|         return nil, err
 | ||
|     }
 | ||
| 
 | ||
|     // 2. 转换为 DTO
 | ||
|     return &dto.UserResponse{
 | ||
|         ID:       user.ID,
 | ||
|         Username: user.Username,
 | ||
|         Email:    user.Email,
 | ||
|         Phone:    user.Phone,
 | ||
|         Status:   string(user.Status),
 | ||
|     }, nil
 | ||
| }
 | ||
| ```
 | ||
| 
 | ||
| ## 🔄 域与域之间的交互
 | ||
| 
 | ||
| ### 事件驱动交互
 | ||
| 
 | ||
| ```go
 | ||
| // 用户注册成功后,通知其他域
 | ||
| type UserRegisteredEvent struct {
 | ||
|     UserID   int64  `json:"user_id"`
 | ||
|     Username string `json:"username"`
 | ||
|     Email    string `json:"email"`
 | ||
| }
 | ||
| 
 | ||
| // 金融域监听用户注册事件,自动创建钱包
 | ||
| func (h *WalletEventHandler) HandleUserRegistered(event UserRegisteredEvent) error {
 | ||
|     // 为新用户创建钱包
 | ||
|     wallet := &entity.Wallet{
 | ||
|         UserID:  event.UserID,
 | ||
|         Balance: 0.0,
 | ||
|         Currency: "CNY",
 | ||
|     }
 | ||
|     return h.walletRepo.Save(context.Background(), wallet)
 | ||
| }
 | ||
| ```
 | ||
| 
 | ||
| ## 📊 Domain 的好处
 | ||
| 
 | ||
| ### 1. **团队协作**
 | ||
| 
 | ||
| ```
 | ||
| 用户域团队:专注用户相关功能
 | ||
| ├── 张三:负责用户注册登录
 | ||
| ├── 李四:负责用户资料管理
 | ||
| └── 王五:负责企业认证
 | ||
| 
 | ||
| 支付域团队:专注支付相关功能
 | ||
| ├── 赵六:负责钱包功能
 | ||
| ├── 孙七:负责支付流程
 | ||
| └── 周八:负责账单生成
 | ||
| ```
 | ||
| 
 | ||
| ### 2. **独立部署**
 | ||
| 
 | ||
| ```bash
 | ||
| # 可以独立部署不同的域
 | ||
| kubectl apply -f user-domain-deployment.yaml
 | ||
| kubectl apply -f payment-domain-deployment.yaml
 | ||
| kubectl apply -f product-domain-deployment.yaml
 | ||
| ```
 | ||
| 
 | ||
| ### 3. **技术选择自由**
 | ||
| 
 | ||
| ```
 | ||
| 用户域:使用 PostgreSQL (复杂查询)
 | ||
| 支付域:使用 MySQL (事务性强)
 | ||
| 数据域:使用 ClickHouse (分析查询)
 | ||
| ```
 | ||
| 
 | ||
| ### 4. **测试隔离**
 | ||
| 
 | ||
| ```go
 | ||
| // 只测试用户域,不需要启动其他域
 | ||
| func TestUserDomain(t *testing.T) {
 | ||
|     // 只需要用户域的依赖
 | ||
|     userRepo := mock.NewUserRepository()
 | ||
|     userService := service.NewUserDomainService(userRepo)
 | ||
| 
 | ||
|     // 测试用户注册逻辑
 | ||
|     user, err := userService.RegisterUser(ctx, "testuser", "test@example.com", "13800138000")
 | ||
|     assert.NoError(t, err)
 | ||
|     assert.Equal(t, "testuser", user.Username)
 | ||
| }
 | ||
| ```
 | ||
| 
 | ||
| ## 🎯 总结
 | ||
| 
 | ||
| **Domain(域)就是按业务功能划分的代码组织方式**:
 | ||
| 
 | ||
| 1. **用户域** - 管理用户相关的所有功能
 | ||
| 2. **支付域** - 管理支付相关的所有功能
 | ||
| 3. **产品域** - 管理产品相关的所有功能
 | ||
| 4. **数据域** - 管理数据查询相关的功能
 | ||
| 
 | ||
| **核心思想**:
 | ||
| 
 | ||
| -   按业务划分,不按技术划分
 | ||
| -   每个域独立开发、测试、部署
 | ||
| -   域之间通过事件或 API 通信
 | ||
| -   团队可以专注于特定业务领域
 | ||
| 
 | ||
| 这样组织代码后,你的项目会更容易理解、维护和扩展!
 |