Skip to content

集成开发规范

概述

集成开发规范定义了如何将第三方服务集成到应用中的标准模式。适用于需要对接外部 API、云服务或开源组件的 Go Web 应用开发。

设计目标

  • 统一性 - 所有集成遵循一致的架构模式和开发规范
  • 可维护性 - 清晰的封装设计,便于理解和维护
  • 可测试性 - 支持 Mock 和单元测试,不依赖真实服务
  • 可扩展性 - 易于新增集成服务或更换服务提供商

Client 层在架构中的位置

Client 层作为 Service 的依赖,封装第三方服务调用,与 GORM 平级:

调用约束

  • 允许:Service 调用 Client
  • 允许:Service 调用 Service(通过依赖注入)
  • 禁止:Handler 直接调用 Client
  • 禁止:Client 调用 Service(避免循环依赖)

集成服务分类

核心业务集成

直接支撑业务功能的第三方服务:

  • 对象存储:COS、S3、OSS 等文件存储服务
  • AI 服务:OpenAI、DeepSeek 等 AI API
  • 消息队列:Kafka、RabbitMQ、Redis 等
  • 缓存服务:Redis、Memcached 等
  • 搜索引擎:Elasticsearch、Meilisearch 等

通知服务集成

用于消息推送和通知的服务:

  • 邮件服务:SMTP、SendGrid、阿里云邮件推送
  • 短信服务:阿里云 SMS、腾讯云 SMS
  • 即时通讯:飞书、钉钉、企业微信机器人
  • 推送服务:APNs、FCM、个推

可观测性集成

监控、日志和性能分析服务:

  • 链路追踪:OpenTelemetry、Jaeger、Zipkin
  • 错误监控:Sentry、Bugsnag
  • 性能分析:Pyroscope、Prometheus
  • 日志聚合:ELK、Grafana Loki

规范列表

快速查找

场景导航

核心原则

1. 单一职责

每个 Client 只负责一个外部服务的交互

go
// ✅ 正确:每个 Client 职责明确
type StorageClient struct {}  // 只负责文件存储
type EmailClient struct {}    // 只负责邮件发送
type SMSClient struct {}      // 只负责短信发送

// ❌ 错误:一个 Client 负责多个服务
type NotificationClient struct {
    // 同时包含邮件和短信功能
}

2. 配置注入

Client 通过配置结构体初始化,不硬编码配置

go
// ✅ 正确:配置注入
func NewStorageClient(cfg *StorageConfig) (*StorageClient, error) {
    return &StorageClient{
        endpoint:  cfg.Endpoint,
        accessKey: cfg.AccessKey,
    }, nil
}

// ❌ 错误:硬编码配置
func NewStorageClient() (*StorageClient, error) {
    return &StorageClient{
        endpoint:  "https://cos.ap-beijing.myqcloud.com",
        accessKey: "AKID...",  // 硬编码密钥
    }, nil
}

3. 错误转换

将第三方错误转换为业务语义错误,隐藏实现细节:

go
// ✅ 正确:转换为业务错误
func (c *StorageClient) Upload(ctx context.Context, file *File) error {
    err := c.cosClient.Upload(ctx, file)
    if err != nil {
        // 转换为业务错误,隐藏 COS 实现细节
        return ErrStorageUploadFailed
    }
    return nil
}

// ❌ 错误:直接返回第三方错误
func (c *StorageClient) Upload(ctx context.Context, file *File) error {
    // 暴露了 COS 的实现细节
    return c.cosClient.Upload(ctx, file)
}

4. 上下文传递

所有 Client 方法必须接受 context.Context

go
// ✅ 正确:接受 context
func (c *AIClient) Generate(ctx context.Context, prompt string) (*Response, error) {
    // 支持超时控制和取消操作
}

// ❌ 错误:不接受 context
func (c *AIClient) Generate(prompt string) (*Response, error) {
    // 无法控制超时和取消
}

开发流程

新增集成服务的步骤

  1. 定义配置 - 在 config/config.go 添加配置结构体
  2. 实现 Client - 在 clients/ 目录创建 Client 实现
  3. 添加到 App - 在 App 结构体中添加 Client 字段
  4. 初始化注入 - 在 App.Init() 中初始化并注入到 Service
  5. 编写测试 - 编写单元测试和集成测试
  6. 编写文档 - 添加使用说明和示例

详见 可扩展性设计

最佳实践

1. 配置校验

在 Client 初始化时验证配置的完整性和有效性

go
func NewClient(cfg *Config) (*Client, error) {
    // ✅ 启动时发现配置错误
    if err := cfg.Validate(); err != nil {
        return nil, fmt.Errorf("invalid config: %w", err)
    }

    client := &Client{config: cfg}
    return client, nil
}

2. 失快原则

配置错误应阻止应用启动,而非运行时失败

go
func (a *App) Init(ctx context.Context, cfg *Config) error {
    // ✅ 初始化失败时应用无法启动
    client, err := NewStorageClient(&cfg.Storage)
    if err != nil {
        return err  // 阻止启动
    }
    a.storageClient = client
    return nil
}

3. 资源管理

实现 Close() 方法支持优雅关闭

go
type Client struct {
    httpClient *http.Client
    conn       *grpc.ClientConn
}

func (c *Client) Close() error {
    if c.conn != nil {
        return c.conn.Close()
    }
    return nil
}

4. 接口抽象

对于可能更换的服务,定义接口而非具体实现

go
// ✅ 定义接口
type Storage interface {
    Upload(ctx context.Context, file *File) (*Result, error)
    Download(ctx context.Context, key string) (io.ReadCloser, error)
}

// 具体实现可以是 COS、S3、OSS 等
type COSStorage struct {}
type S3Storage struct {}
type OSSStorage struct {}

相关规范