5.4 KiB
5.4 KiB
用户域实体优化总结
🎯 优化目标
将 User 实体从"贫血模型"升级为"充血模型",将业务逻辑从 Service 层迁移到实体层,实现更好的封装和职责分离。
✅ 完成的优化
1. 实体业务方法增强
密码管理方法
// 修改密码(包含完整的业务验证)
func (u *User) ChangePassword(oldPassword, newPassword, confirmPassword string) error
// 验证密码
func (u *User) CheckPassword(password string) bool
// 设置密码(用于注册或重置)
func (u *User) SetPassword(password string) error
手机号管理方法
// 验证手机号格式
func (u *User) IsValidPhone() bool
// 设置手机号(包含格式验证)
func (u *User) SetPhone(phone string) error
// 获取脱敏手机号
func (u *User) GetMaskedPhone() string
用户状态检查方法
// 检查用户是否可以登录
func (u *User) CanLogin() bool
// 检查用户是否活跃
func (u *User) IsActive() bool
// 检查用户是否已删除
func (u *User) IsDeleted() bool
2. 业务规则验证
密码强度验证
- 长度要求:8-128 位
- 必须包含数字
- 必须包含字母
- 必须包含特殊字符
手机号格式验证
- 11 位数字
- 以 1 开头
- 第二位为 3-9
业务不变性验证
- 新密码不能与旧密码相同
- 确认密码必须匹配
- 用户状态检查
3. 工厂方法
// 创建新用户的工厂方法
func NewUser(phone, password string) (*User, error)
4. 静态工具方法
// 验证手机号格式(静态方法)
func IsValidPhoneFormat(phone string) bool
// 检查是否为验证错误
func IsValidationError(err error) bool
🔄 Service 层重构
优化前的问题
// 业务逻辑集中在Service中
func (s *UserService) ChangePassword(ctx context.Context, userID string, req *dto.ChangePasswordRequest) error {
// 验证新密码确认
if req.NewPassword != req.ConfirmNewPassword { ... }
// 验证当前密码
if !s.checkPassword(req.OldPassword, user.Password) { ... }
// 哈希新密码
hashedPassword, err := s.hashPassword(req.NewPassword)
// 更新密码
user.Password = hashedPassword
return s.repo.Update(ctx, user)
}
优化后的改进
// Service只负责协调,业务逻辑委托给实体
func (s *UserService) ChangePassword(ctx context.Context, userID string, req *dto.ChangePasswordRequest) error {
// 1. 获取用户信息
user, err := s.repo.GetByID(ctx, userID)
if err != nil {
return fmt.Errorf("用户不存在: %w", err)
}
// 2. 执行业务逻辑(委托给实体)
if err := user.ChangePassword(req.OldPassword, req.NewPassword, req.ConfirmNewPassword); err != nil {
return err
}
// 3. 保存用户
return s.repo.Update(ctx, user)
}
📊 优化效果对比
| 方面 | 优化前 | 优化后 |
|---|---|---|
| 业务逻辑位置 | Service 层 | 实体层 |
| 代码复用性 | 低 | 高 |
| 测试难度 | 需要 Mock 仓储 | 可直接测试实体 |
| 职责分离 | 不清晰 | 清晰 |
| 维护性 | 一般 | 优秀 |
🧪 测试覆盖
单元测试
- ✅ 密码修改功能测试
- ✅ 密码验证功能测试
- ✅ 手机号设置功能测试
- ✅ 手机号脱敏功能测试
- ✅ 手机号格式验证测试
- ✅ 用户创建工厂方法测试
测试结果
=== RUN TestUser_ChangePassword
--- PASS: TestUser_ChangePassword (0.57s)
=== RUN TestUser_CheckPassword
--- PASS: TestUser_CheckPassword (0.16s)
=== RUN TestUser_SetPhone
--- PASS: TestUser_SetPhone (0.00s)
=== RUN TestUser_GetMaskedPhone
--- PASS: TestUser_GetMaskedPhone (0.00s)
=== RUN TestIsValidPhoneFormat
--- PASS: TestIsValidPhoneFormat (0.00s)
=== RUN TestNewUser
--- PASS: TestNewUser (0.08s)
PASS
🚀 新增功能
1. 用户信息更新
func (s *UserService) UpdateUserProfile(ctx context.Context, userID string, req *dto.UpdateProfileRequest) (*entities.User, error)
2. 用户停用
func (s *UserService) DeactivateUser(ctx context.Context, userID string) error
3. 软删除支持
func (r *UserRepository) SoftDelete(ctx context.Context, id string) error
func (r *UserRepository) Restore(ctx context.Context, id string) error
📈 架构改进
1. 更好的封装
- 业务规则与数据在一起
- 减少外部依赖
- 提高内聚性
2. 更清晰的职责
- 实体:业务逻辑和验证
- Service:协调和事务管理
- Repository:数据访问
3. 更容易测试
- 实体方法可以独立测试
- 不需要复杂的 Mock 设置
- 测试覆盖更全面
🎉 总结
这次优化成功实现了:
- ✅ 充血模型 - 实体包含丰富的业务方法
- ✅ 职责分离 - Service 专注于协调,实体专注于业务逻辑
- ✅ 更好的封装 - 业务规则与数据紧密耦合
- ✅ 更容易测试 - 实体方法可以独立测试
- ✅ 代码复用 - 业务逻辑可以在不同场景下复用
这是一个成功的"轻量级 DDD"实践,在保持架构简单的同时,显著提升了代码质量和可维护性!