集成开发规范
概述
集成开发规范定义了如何将第三方服务集成到应用中的标准模式。适用于需要对接外部 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
规范列表
- Client 层设计 - Client 封装规范、方法签名、目录组织
- 配置管理 - 集成服务配置定义、校验、注入
- 错误处理 - 错误分类、错误转换、重试策略
- 可扩展性设计 - 接口抽象、工厂模式、新增集成步骤
快速查找
场景导航
- 集成对象存储服务 → Client 层设计
- 集成 AI API → Client 层设计
- 配置第三方服务 → 配置管理
- 处理第三方错误 → 错误处理
- 更换服务提供商 → 可扩展性设计
- 添加新的集成 → 可扩展性设计
核心原则
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) {
// 无法控制超时和取消
}开发流程
新增集成服务的步骤
- 定义配置 - 在
config/config.go添加配置结构体 - 实现 Client - 在
clients/目录创建 Client 实现 - 添加到 App - 在
App结构体中添加 Client 字段 - 初始化注入 - 在
App.Init()中初始化并注入到 Service - 编写测试 - 编写单元测试和集成测试
- 编写文档 - 添加使用说明和示例
详见 可扩展性设计
最佳实践
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 {}相关规范
- 分层架构设计 - Client 在架构中的位置
- 依赖注入规范 - Client 依赖管理
- Service 层设计 - Service 如何调用 Client
- 错误处理规范 - 错误处理最佳实践