9.7 KiB
9.7 KiB
DDD规范企业认证信息自动填充实现总结
概述
根据DDD(领域驱动设计)架构规范,重新设计了企业认证信息自动填充功能。在DDD中,跨域操作应该通过应用服务层来协调,而不是在领域服务层直接操作其他领域的仓储。
DDD架构规范
1. 领域边界原则
- 领域服务层:只能操作本领域的仓储和实体
- 应用服务层:负责跨域协调,调用不同领域的服务
- 聚合根:每个领域有自己的聚合根,不能直接访问其他领域的聚合根
2. 依赖方向
应用服务层 → 领域服务层 → 仓储层
↓
跨域协调
重新设计架构
1. 领域服务层(Finance Domain)
UserInvoiceInfoService接口更新
type UserInvoiceInfoService interface {
// 基础方法
GetUserInvoiceInfo(ctx context.Context, userID string) (*entities.UserInvoiceInfo, error)
CreateOrUpdateUserInvoiceInfo(ctx context.Context, userID string, invoiceInfo *value_objects.InvoiceInfo) (*entities.UserInvoiceInfo, error)
// 新增:包含企业认证信息的方法
GetUserInvoiceInfoWithEnterpriseInfo(ctx context.Context, userID string, companyName, taxpayerID string) (*entities.UserInvoiceInfo, error)
CreateOrUpdateUserInvoiceInfoWithEnterpriseInfo(ctx context.Context, userID string, invoiceInfo *value_objects.InvoiceInfo, companyName, taxpayerID string) (*entities.UserInvoiceInfo, error)
ValidateInvoiceInfo(ctx context.Context, invoiceInfo *value_objects.InvoiceInfo, invoiceType value_objects.InvoiceType) error
DeleteUserInvoiceInfo(ctx context.Context, userID string) error
}
实现特点
- 移除跨域依赖:不再直接依赖
user_repo.UserRepository - 参数化设计:通过方法参数接收企业认证信息
- 保持纯净性:领域服务只处理本领域的业务逻辑
2. 应用服务层(Application Layer)
InvoiceApplicationService更新
type InvoiceApplicationServiceImpl struct {
invoiceRepo finance_repo.InvoiceApplicationRepository
userInvoiceInfoRepo finance_repo.UserInvoiceInfoRepository
userRepo user_repo.UserRepository
userAggregateService user_service.UserAggregateService // 新增:用户聚合服务
rechargeRecordRepo finance_repo.RechargeRecordRepository
walletRepo finance_repo.WalletRepository
invoiceDomainService services.InvoiceDomainService
invoiceAggregateService services.InvoiceAggregateService
userInvoiceInfoService services.UserInvoiceInfoService
storageService *storage.QiNiuStorageService
logger *zap.Logger
}
跨域协调逻辑
func (s *InvoiceApplicationServiceImpl) GetUserInvoiceInfo(ctx context.Context, userID string) (*dto.InvoiceInfoResponse, error) {
// 1. 通过用户聚合服务获取企业认证信息
user, err := s.userAggregateService.GetUserWithEnterpriseInfo(ctx, userID)
if err != nil {
return nil, fmt.Errorf("获取用户企业认证信息失败: %w", err)
}
// 2. 提取企业认证信息
var companyName, taxpayerID string
var companyNameReadOnly, taxpayerIDReadOnly bool
if user.EnterpriseInfo != nil {
companyName = user.EnterpriseInfo.CompanyName
taxpayerID = user.EnterpriseInfo.UnifiedSocialCode
companyNameReadOnly = true
taxpayerIDReadOnly = true
}
// 3. 调用领域服务,传入企业认证信息
userInvoiceInfo, err := s.userInvoiceInfoService.GetUserInvoiceInfoWithEnterpriseInfo(ctx, userID, companyName, taxpayerID)
if err != nil {
return nil, err
}
// 4. 构建响应DTO
return &dto.InvoiceInfoResponse{
CompanyName: userInvoiceInfo.CompanyName,
TaxpayerID: userInvoiceInfo.TaxpayerID,
// ... 其他字段
CompanyNameReadOnly: companyNameReadOnly,
TaxpayerIDReadOnly: taxpayerIDReadOnly,
}, nil
}
3. 依赖注入更新
容器配置
// 用户聚合服务
fx.Annotate(
user_service.NewUserAggregateService,
fx.ResultTags(`name:"userAggregateService"`),
),
// 用户开票信息服务(移除userRepo依赖)
fx.Annotate(
finance_service.NewUserInvoiceInfoService,
fx.ParamTags(
`name:"userInvoiceInfoRepo"`,
),
fx.ResultTags(`name:"userInvoiceInfoService"`),
),
// 发票应用服务(添加userAggregateService依赖)
fx.Annotate(
finance.NewInvoiceApplicationService,
fx.As(new(finance.InvoiceApplicationService)),
fx.ParamTags(
`name:"invoiceRepo"`,
`name:"userInvoiceInfoRepo"`,
`name:"userRepo"`,
`name:"userAggregateService"`, // 新增
`name:"rechargeRecordRepo"`,
`name:"walletRepo"`,
`name:"domainService"`,
`name:"aggregateService"`,
`name:"userInvoiceInfoService"`,
`name:"storageService"`,
`name:"logger"`,
),
),
架构优势
1. 符合DDD规范
- 领域边界清晰:每个领域只处理自己的业务逻辑
- 依赖方向正确:应用服务层负责跨域协调
- 聚合根隔离:不同领域的聚合根不直接交互
2. 可维护性
- 职责分离:领域服务专注于本领域逻辑
- 易于测试:可以独立测试每个领域服务
- 扩展性好:新增跨域功能时只需修改应用服务层
3. 业务逻辑清晰
- 数据流向明确:企业认证信息 → 应用服务 → 开票信息
- 错误处理统一:在应用服务层统一处理跨域错误
- 权限控制集中:在应用服务层统一控制访问权限
工作流程
1. 获取开票信息流程
用户请求 → 应用服务层
↓
调用UserAggregateService.GetUserWithEnterpriseInfo()
↓
获取企业认证信息
↓
调用UserInvoiceInfoService.GetUserInvoiceInfoWithEnterpriseInfo()
↓
返回开票信息(包含企业认证信息)
2. 更新开票信息流程
用户请求 → 应用服务层
↓
调用UserAggregateService.GetUserWithEnterpriseInfo()
↓
验证企业认证状态
↓
调用UserInvoiceInfoService.CreateOrUpdateUserInvoiceInfoWithEnterpriseInfo()
↓
保存开票信息(企业认证信息自动填充)
技术实现要点
1. 接口设计
- 向后兼容:保留原有的基础方法
- 功能扩展:新增包含企业认证信息的方法
- 参数传递:通过方法参数传递跨域数据
2. 错误处理
- 分层处理:在应用服务层处理跨域错误
- 错误传播:领域服务层错误向上传播
- 用户友好:提供清晰的错误信息
3. 性能优化
- 减少查询:应用服务层缓存企业认证信息
- 批量操作:支持批量获取和更新
- 异步处理:非关键路径支持异步处理
代码示例
1. 领域服务实现
// GetUserInvoiceInfoWithEnterpriseInfo 获取用户开票信息(包含企业认证信息)
func (s *UserInvoiceInfoServiceImpl) GetUserInvoiceInfoWithEnterpriseInfo(ctx context.Context, userID string, companyName, taxpayerID string) (*entities.UserInvoiceInfo, error) {
info, err := s.userInvoiceInfoRepo.FindByUserID(ctx, userID)
if err != nil {
return nil, fmt.Errorf("获取用户开票信息失败: %w", err)
}
// 如果没有找到开票信息记录,创建新的实体
if info == nil {
info = &entities.UserInvoiceInfo{
ID: uuid.New().String(),
UserID: userID,
CompanyName: "",
TaxpayerID: "",
BankName: "",
BankAccount: "",
CompanyAddress: "",
CompanyPhone: "",
ReceivingEmail: "",
}
}
// 使用传入的企业认证信息填充公司名称和纳税人识别号
if companyName != "" {
info.CompanyName = companyName
}
if taxpayerID != "" {
info.TaxpayerID = taxpayerID
}
return info, nil
}
2. 应用服务实现
func (s *InvoiceApplicationServiceImpl) UpdateUserInvoiceInfo(ctx context.Context, userID string, req UpdateInvoiceInfoRequest) error {
// 获取用户企业认证信息
user, err := s.userAggregateService.GetUserWithEnterpriseInfo(ctx, userID)
if err != nil {
return fmt.Errorf("获取用户企业认证信息失败: %w", err)
}
// 检查用户是否有企业认证信息
if user.EnterpriseInfo == nil {
return fmt.Errorf("用户未完成企业认证,无法创建开票信息")
}
// 创建开票信息对象
invoiceInfo := value_objects.NewInvoiceInfo(
"", // 公司名称将由服务层从企业认证信息中获取
"", // 纳税人识别号将由服务层从企业认证信息中获取
req.BankName,
req.BankAccount,
req.CompanyAddress,
req.CompanyPhone,
req.ReceivingEmail,
)
// 使用包含企业认证信息的方法
_, err = s.userInvoiceInfoService.CreateOrUpdateUserInvoiceInfoWithEnterpriseInfo(
ctx,
userID,
invoiceInfo,
user.EnterpriseInfo.CompanyName,
user.EnterpriseInfo.UnifiedSocialCode,
)
return err
}
总结
通过按照DDD规范重新设计,我们实现了:
- ✅ 架构规范:严格遵循DDD的领域边界和依赖方向
- ✅ 职责分离:领域服务专注于本领域逻辑,应用服务负责跨域协调
- ✅ 可维护性:代码结构清晰,易于理解和维护
- ✅ 可扩展性:新增跨域功能时只需修改应用服务层
- ✅ 业务逻辑:企业认证信息自动填充功能完整实现
这种设计既满足了业务需求,又符合DDD架构规范,是一个优秀的架构实现。