Golang中的六边形架构与接口污染问题探讨
Golang中的六边形架构与接口污染问题探讨 我正在一个项目中,使用端口与适配器概念的规范版本实现,我们只有三层:模型层(实体、DTO等)、领域层(业务逻辑)、基础设施层(处理器、PostgreSQL)。
但是,在领域层中,我们有许多臃肿的接口,包含了许多实际上可能是辅助函数的长方法名。这给我带来了糟糕的开发体验,因为这些接口必须被实现,感觉就像不必要的官僚主义或预防性抽象。
这种在Go中采用六边形架构的传统方法正确吗?我有一年的Go经验。
2 回复
你好 @adrianolmedo,
臃肿的接口是 Java 风格的。我不认为六边形架构(HA)要求接口必须是臃肿的。HA 的核心是关注点分离,因此只要你实现了这个目标,就应该没问题。所以我认为在 Go 中使用 HA 没有问题。问题似乎更多在于如何让接口更符合 Go 的风格(也就是说,尽可能将它们拆分成更小的接口)。
更多关于Golang中的六边形架构与接口污染问题探讨的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
在Go中实现六边形架构时,接口臃肿确实是常见问题。传统方法往往过度抽象,导致接口包含过多方法。更符合Go习惯的做法是定义小而专注的接口,让接口自然演进。
示例代码展示更简洁的接口设计:
// 传统臃肿接口
type UserRepository interface {
Create(ctx context.Context, user *User) error
Update(ctx context.Context, user *User) error
Delete(ctx context.Context, id string) error
FindByID(ctx context.Context, id string) (*User, error)
FindByEmail(ctx context.Context, email string) (*User, error)
FindAll(ctx context.Context, filter Filter) ([]*User, error)
Count(ctx context.Context, filter Filter) (int, error)
// 更多方法...
}
// 改进后的小接口
type UserSaver interface {
Save(ctx context.Context, user *User) error
}
type UserFinder interface {
FindByID(ctx context.Context, id string) (*User, error)
FindByEmail(ctx context.Context, email string) (*User, error)
}
type UserRemover interface {
Remove(ctx context.Context, id string) error
}
// 领域层使用具体的小接口
type UserService struct {
saver UserSaver
finder UserFinder
remover UserRemover
}
func (s *UserService) RegisterUser(ctx context.Context, email, name string) error {
user := &User{Email: email, Name: name}
return s.saver.Save(ctx, user)
}
基础设施层实现可以按需组合:
// PostgreSQL实现
type PostgresUserStore struct {
db *sql.DB
}
func (p *PostgresUserStore) Save(ctx context.Context, user *User) error {
// 具体实现
_, err := p.db.ExecContext(ctx,
"INSERT INTO users (id, email, name) VALUES ($1, $2, $3)",
user.ID, user.Email, user.Name)
return err
}
func (p *PostgresUserStore) FindByID(ctx context.Context, id string) (*User, error) {
// 具体实现
row := p.db.QueryRowContext(ctx,
"SELECT id, email, name FROM users WHERE id = $1", id)
// 解析结果...
}
// 实现所有需要的接口
var _ UserSaver = (*PostgresUserStore)(nil)
var _ UserFinder = (*PostgresUserStore)(nil)
var _ UserRemover = (*PostgresUserStore)(nil)
对于辅助函数,可以提取到独立的包或结构体:
// 将辅助逻辑提取到独立组件
type UserValidator struct{}
func (v *UserValidator) ValidateEmail(email string) error {
if !strings.Contains(email, "@") {
return errors.New("invalid email format")
}
return nil
}
func (v *UserValidator) ValidateName(name string) error {
if len(name) < 2 {
return errors.New("name too short")
}
return nil
}
这种设计减少了接口方法数量,提高了代码的可测试性和可维护性。接口应该由消费者定义,而不是提供者。只有当领域层真正需要某个方法时,才在接口中声明它。

