模块化架构
VEF Framework 采用模块化架构,将业务领域组织为独立的模块,实现清晰的关注点分离。
模块概念
模块是一组相关功能的集合,包含:
- 模型(Models): 数据库实体定义
- 参数(Payloads): 请求参数和响应结构
- 资源(Resources): API 端点定义
- 服务(Services): 业务逻辑实现
模块结构
推荐的模块目录结构:
internal/
└── modules/
└── user/ # User module
├── module.go # Module definition
├── models/
│ └── user.go # User model
├── payloads/
│ └── user.go # Request/response payloads
├── resources/
│ └── user.go # API resource
└── services/
└── user_service.go # Business logic定义模块
基本模块定义
go
// internal/modules/user/module.go
package user
import (
"my-app/internal/modules/user/resources"
"my-app/internal/modules/user/services"
"github.com/ilxqx/vef-framework-go/vef"
)
// Module returns the user module with all its dependencies
func Module() vef.ModuleFunc {
return vef.Module(
// Register services
vef.Provide(services.NewUserService),
// Register API resources
vef.ProvideResource(resources.NewUserResource),
)
}带初始化逻辑的模块
go
// internal/modules/notification/module.go
package notification
import (
"context"
"my-app/internal/modules/notification/services"
"my-app/internal/modules/notification/subscribers"
"github.com/ilxqx/vef-framework-go/event"
"github.com/ilxqx/vef-framework-go/log"
"github.com/ilxqx/vef-framework-go/vef"
)
func Module() vef.ModuleFunc {
return vef.Module(
// Register services
vef.Provide(services.NewEmailService),
vef.Provide(services.NewSmsService),
// Register event subscribers
vef.Provide(subscribers.NewUserEventSubscriber),
// Initialize module on startup
vef.Invoke(func(
bus event.Bus,
logger log.Logger,
subscriber *subscribers.UserEventSubscriber,
) {
logger.Info("Notification module initialized")
}),
)
}模块组合
在主程序中组合所有模块:
go
// cmd/main.go
package main
import (
"my-app/internal/modules/user"
"my-app/internal/modules/order"
"my-app/internal/modules/notification"
"my-app/internal/modules/report"
"github.com/ilxqx/vef-framework-go/vef"
)
func main() {
vef.Run(
// Core modules (no dependencies on other modules)
user.Module(),
// Modules that depend on core modules
order.Module(),
notification.Module(),
// Modules that depend on multiple modules
report.Module(),
)
}模块依赖
跨模块依赖
模块可以依赖其他模块提供的服务:
go
// internal/modules/order/services/order_service.go
package services
import (
userServices "my-app/internal/modules/user/services"
"github.com/ilxqx/vef-framework-go/orm"
)
// OrderService depends on UserService from user module
type OrderService struct {
db orm.Db
userService *userServices.UserService
}
func NewOrderService(
db orm.Db,
userService *userServices.UserService,
) *OrderService {
return &OrderService{
db: db,
userService: userService,
}
}
func (s *OrderService) CreateOrder(ctx context.Context, userId string) error {
// Use userService to validate user
user, err := s.userService.GetById(ctx, userId)
if err != nil {
return err
}
// Create order logic...
return nil
}依赖顺序
在 main.go 中,模块应按依赖顺序注册:
go
func main() {
vef.Run(
// 1. Base modules (no dependencies)
user.Module(),
product.Module(),
// 2. Modules depending on base modules
order.Module(), // Depends on user, product
inventory.Module(), // Depends on product
// 3. Modules depending on multiple modules
report.Module(), // Depends on order, inventory
)
}完整模块示例
模型定义
go
// internal/modules/user/models/user.go
package models
import (
"github.com/ilxqx/vef-framework-go/orm"
"github.com/ilxqx/vef-framework-go/null"
)
// User represents a user entity in the database
type User struct {
orm.Model
Username string `bun:"username,notnull,unique" json:"username"`
Email string `bun:"email,notnull,unique" json:"email"`
Password string `bun:"password,notnull" json:"-"`
DisplayName null.String `bun:"display_name" json:"displayName"`
IsActive bool `bun:"is_active,notnull,default:true" json:"isActive"`
}参数定义
go
// internal/modules/user/payloads/user.go
package payloads
import "github.com/ilxqx/vef-framework-go/api"
// UserSearch defines search parameters for user queries
type UserSearch struct {
api.P
Username string `json:"username" search:"contains"`
Email string `json:"email" search:"contains"`
IsActive *bool `json:"isActive" search:"eq"`
}
// UserCreateParams defines parameters for creating a user
type UserCreateParams struct {
api.P
Username string `json:"username" validate:"required,alphanum,max=32"`
Email string `json:"email" validate:"required,email"`
Password string `json:"password" validate:"required,min=6,max=32"`
DisplayName string `json:"displayName" validate:"max=64"`
}
// UserUpdateParams defines parameters for updating a user
type UserUpdateParams struct {
api.P
Id string `json:"id"`
DisplayName string `json:"displayName" validate:"max=64"`
IsActive *bool `json:"isActive"`
}服务定义
go
// internal/modules/user/services/user_service.go
package services
import (
"context"
"my-app/internal/modules/user/models"
"github.com/ilxqx/vef-framework-go/log"
"github.com/ilxqx/vef-framework-go/orm"
"golang.org/x/crypto/bcrypt"
)
// UserService handles user business logic
type UserService struct {
db orm.Db
logger log.Logger
}
func NewUserService(db orm.Db, logger log.Logger) *UserService {
return &UserService{
db: db,
logger: logger,
}
}
// GetById retrieves a user by ID
func (s *UserService) GetById(ctx context.Context, id string) (*models.User, error) {
var user models.User
err := s.db.NewSelect().
Model(&user).
Where(func(cb orm.ConditionBuilder) {
cb.Equals("id", id)
}).
Scan(ctx)
if err != nil {
return nil, err
}
return &user, nil
}
// HashPassword hashes a plain text password
func (s *UserService) HashPassword(password string) (string, error) {
hashed, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {
return "", err
}
return string(hashed), nil
}资源定义
go
// internal/modules/user/resources/user.go
package resources
import (
"my-app/internal/modules/user/models"
"my-app/internal/modules/user/payloads"
"my-app/internal/modules/user/services"
"github.com/gofiber/fiber/v3"
"github.com/ilxqx/vef-framework-go/api"
"github.com/ilxqx/vef-framework-go/apis"
"github.com/ilxqx/vef-framework-go/orm"
"github.com/ilxqx/vef-framework-go/result"
)
// UserResource defines the user API resource
type UserResource struct {
api.Resource
apis.FindPageApi[models.User, payloads.UserSearch]
apis.CreateApi[models.User, payloads.UserCreateParams]
apis.UpdateApi[models.User, payloads.UserUpdateParams]
apis.DeleteApi[models.User]
userService *services.UserService
}
func NewUserResource(userService *services.UserService) api.Resource {
return &UserResource{
Resource: api.NewResource(
"app/sys/user",
api.WithApis(
api.Spec{Action: "change_password"},
),
),
FindPageApi: apis.NewFindPageApi[models.User, payloads.UserSearch](),
CreateApi: apis.NewCreateApi[models.User, payloads.UserCreateParams]().
WithPreCreate(func(model *models.User, params *payloads.UserCreateParams, ctx fiber.Ctx, db orm.Db) error {
// Hash password before creating user
hashed, err := userService.HashPassword(params.Password)
if err != nil {
return err
}
model.Password = hashed
return nil
}),
UpdateApi: apis.NewUpdateApi[models.User, payloads.UserUpdateParams](),
DeleteApi: apis.NewDeleteApi[models.User](),
userService: userService,
}
}
// ChangePassword handles password change requests
func (r *UserResource) ChangePassword(
ctx fiber.Ctx,
db orm.Db,
params payloads.ChangePasswordParams,
) error {
// Custom handler implementation
hashed, err := r.userService.HashPassword(params.NewPassword)
if err != nil {
return result.Err("Failed to hash password").Response(ctx)
}
_, err = db.NewUpdate().
Model(&models.User{}).
Set("password = ?", hashed).
Where(func(cb orm.ConditionBuilder) {
cb.Equals("id", params.Id)
}).
Exec(ctx.Context())
if err != nil {
return result.Err("Failed to update password").Response(ctx)
}
return result.Ok().Response(ctx)
}模块定义
go
// internal/modules/user/module.go
package user
import (
"my-app/internal/modules/user/resources"
"my-app/internal/modules/user/services"
"github.com/ilxqx/vef-framework-go/vef"
)
// Module returns the user module
func Module() vef.ModuleFunc {
return vef.Module(
// Services
vef.Provide(services.NewUserService),
// Resources
vef.ProvideResource(resources.NewUserResource),
)
}模块化优势
1. 关注点分离
每个模块专注于特定的业务领域,代码组织清晰。
2. 团队协作
不同团队可以独立开发不同模块,减少代码冲突。
3. 可测试性
模块可以独立测试,便于编写单元测试和集成测试。
4. 可复用性
模块可以在不同项目中复用。
5. 可维护性
修改一个模块不会影响其他模块,降低维护成本。