tianyuan-api-server/Domain域详解.md
2025-07-13 20:37:12 +08:00

9.8 KiB
Raw Permalink Blame History

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 层的职责

// 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 (领域服务)

// 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 接口 (仓储模式)

// 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)

// 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
}

🔄 域与域之间的交互

事件驱动交互

// 用户注册成功后,通知其他域
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. 独立部署

# 可以独立部署不同的域
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. 测试隔离

// 只测试用户域,不需要启动其他域
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 通信
  • 团队可以专注于特定业务领域

这样组织代码后,你的项目会更容易理解、维护和扩展!