服务
介绍
服务代表领域/业务层。这是应用程序核心逻辑和价值所在的地方。服务是实现业务逻辑(例如 UserService
管理 User
资源)或对功能(例如:AuthService
管理认证)或外部依赖(例如:其他微服务、第三方 API)的抽象的结构。
这一层在其他两层(表示层和数据层)之间架起桥梁。它接受 DTO 作为输入并返回 DTO 作为输出。当需要与数据层通信时,它使用模型。
实现
服务必须实现 goyave.Service
接口:即具有返回用于标识和检索服务的常量的 Name()
方法。
服务名称在 service/service.go
中定义。该文件将如下所示:
go
package service
// 已实现服务的名称。
const (
User = "user"
Product = "product"
Auth = "auth"
//...
)
INFO
在专用包中定义常量形式的服务名称有助于不在包之间创建强依赖关系。
完成后,您可以实现您的服务。每个服务在 service
中应有自己的包,以其使用的资源命名,使用单数形式。
其依赖项应使用接口定义并作为构造函数参数接受。服务可以依赖于仓库,也可以依赖于其他服务,也由接口定义。
完整示例:
go
// service/user/user.go
package user
import (
"context"
"goyave.dev/goyave/v5/database"
"goyave.dev/goyave/v5/util/errors"
"goyave.dev/goyave/v5/util/typeutil"
"my-project/database/model"
"my-project/dto"
"my-project/service"
)
// Repository 定义了此服务在操作用户时依赖的数据库函数。
type Repository interface {
First(ctx context.Context, id int64) (*model.User, error)
Paginate(ctx context.Context, page int, pageSize int) (*database.Paginator[*model.User], error)
}
// Service 用于用户资源。
type Service struct {
repository Repository
}
// NewService 创建一个新的用户服务。
func NewService(repository Repository) *Service {
return &Service{
repository: repository,
}
}
// First 返回由给定 ID 标识的第一个用户。
func (s *Service) First(ctx context.Context, id int64) (*dto.User, error) {
u, err := s.repository.First(ctx, id)
return typeutil.MustConvert[*dto.User](u), errors.New(err)
}
// Paginate 返回一个分页器,包含所有匹配给定过滤请求的记录。
func (s *Service) Paginate(ctx context.Context, page, pageSize int) (*database.PaginatorDTO[*dto.User], error) {
paginator, err := s.repository.Paginate(ctx, page, pageSize)
return typeutil.MustConvert[*database.PaginatorDTO[*dto.User]](paginator), errors.New(err)
}
// Name 返回服务名称。
func (s *Service) Name() string {
return service.User
}
服务容器
Goyave 提供了一个简单的服务依赖容器,组件可以随时从服务器访问(最好从它们的 Init()
方法访问)。在生命周期的初始化阶段,服务被创建并注册到此容器中。
任何服务都可以在初始化生命周期步骤中注册:
go
func registerServices(server *goyave.Server) {
server.Logger.Info("注册服务")
userRepository := repository.NewUser(server.DB())
userService := user.NewService(userRepository)
server.RegisterService(userService)
}
依赖于此服务的组件然后可以使用 Service()
访问器检索它,并对结果进行类型断言:
go
server.Service(service.User).(MyServiceInterface)
LookupService()
访问器提供了一种更安全的方式来检索可能未注册的服务:
go
userService, ok := server.LookupService(service.User)