[ Golang 中的 DDD 实践] 仓储
译者:baiyutang
现在很难想象编写应用程序在运行时不访问存储,可能甚至不需要编写部署脚本,他们需要访问配置文件,在某种方式上,仍然是存储类型。
每当你编写应用解决真实业务世界的问题时,你需要连接数据库、外部接口、一些缓存系统,等等,这是不可避免的。
在某种角度,使用 DDD 模式解决这些需求并不奇怪。当然,DDD 并没有发明“仓库”和很多其他文献中的器具,但是 DDD 变得更清晰。
防腐层
领域驱动设计一个你可以应用在软件开发很多方面和地方的法则。但是,主要的焦点还是应该放在领域层,我们业务逻辑所在的地方。
仓储总代表连接外部世界而保持的技术细节的结构,它已经不属于业务逻辑了。
但是,有时,我们需要从领域层访问仓储。因为领域层是不与其他层通信的底层。我们定义仓储为接口。
import (
"context"
"github.com/google/uuid"
)
type Customer struct {
ID uuid.UUID
//
// some fields
//
}
type Customers []Customer
type CustomerRepository interface {
GetCustomer(ctx context.Context, ID uuid.UUID) (*Customer, error)
SearchCustomers(ctx context.Context, specification CustomerSpecification) (Customers, int, error)
SaveCustomer(ctx context.Context, customer Customer) (*Customer, error)
UpdateCustomer(ctx context.Context, customer Customer) (*Customer, error)
DeleteCustomer(ctx context.Context, ID uuid.UUID) (*Customer, error)
}
被我们成为契约的接口,他定义了我们能够在领域调用的方法签名。在上面的例子中,我们能发现定义了 CRUD 方法的简单接口。
当我们定义了仓储像这样的接口,我们就能在领域层内任何地方使用它。它总是预期并返回我们的实体,在这个案例中, 和 (“我”喜欢在 Go 中定义特定的集合,以便为他们附加不同的方法)。
实体不持有一下存储类型的任何信息。没有定义 JSON 结构的 Go 标签,Gorm 列或其他任何类似。为此,我们使用基础设施层。
// domain layer
type CustomerRepository interface {
GetCustomer(ctx context.Context, ID uuid.UUID) (*Customer, error)
SearchCustomers(ctx context.Context, specification CustomerSpecification) (Customers, int, error)
SaveCustomer(ctx context.Context, customer Customer) (*Customer, error)
UpdateCustomer(ctx context.Context, customer Customer) (*Customer, error)
DeleteCustomer(ctx context.Context, ID uuid.UUID) (*Customer, error)
}
// infrastructure layer
import (
"context"
"github.com/google/uuid"
"gorm.io/gorm"
)
type CustomerGorm struct {
ID uint `gorm:"primaryKey;column:id"`
UUID string `gorm:"uniqueIndex;column:uuid"`
//
// some fields
//
}
func (c CustomerGorm) ToEntity() (model.Customer, error) {
parsed, err := uuid.Parse(c.UUID)
if err != nil {
return Customer{}, err
}
return model.Customer{
ID: parsed,
//
// some fields
//
}, nil
}
type CustomerRepository struct {
connection *gorm.DB
}
func (r *CustomerRepository) GetCustomer(ctx context.Context, ID uuid.UUID) (*model.Customer, error) {
var row CustomerGorm
err := r.connection.WithContext(ctx).Where("uuid = ?", ID).First(&row).Error
if err != nil {
return nil, err
}
customer, err := row.ToEntity()
if err != nil {
return nil, err
}
return &customer, nil
}
//
// other methods
//
在上面的例子中,你能看到 的实现片段。