# DDD规范企业认证信息自动填充实现总结 ## 概述 根据DDD(领域驱动设计)架构规范,重新设计了企业认证信息自动填充功能。在DDD中,跨域操作应该通过应用服务层来协调,而不是在领域服务层直接操作其他领域的仓储。 ## DDD架构规范 ### 1. 领域边界原则 - **领域服务层**:只能操作本领域的仓储和实体 - **应用服务层**:负责跨域协调,调用不同领域的服务 - **聚合根**:每个领域有自己的聚合根,不能直接访问其他领域的聚合根 ### 2. 依赖方向 ``` 应用服务层 → 领域服务层 → 仓储层 ↓ 跨域协调 ``` ## 重新设计架构 ### 1. 领域服务层(Finance Domain) #### `UserInvoiceInfoService`接口更新 ```go 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`更新 ```go 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 } ``` #### 跨域协调逻辑 ```go 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. 依赖注入更新 #### 容器配置 ```go // 用户聚合服务 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. 领域服务实现 ```go // 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. 应用服务实现 ```go 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规范重新设计,我们实现了: 1. ✅ **架构规范**:严格遵循DDD的领域边界和依赖方向 2. ✅ **职责分离**:领域服务专注于本领域逻辑,应用服务负责跨域协调 3. ✅ **可维护性**:代码结构清晰,易于理解和维护 4. ✅ **可扩展性**:新增跨域功能时只需修改应用服务层 5. ✅ **业务逻辑**:企业认证信息自动填充功能完整实现 这种设计既满足了业务需求,又符合DDD架构规范,是一个优秀的架构实现。