Skip to content

模块化架构

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. 可维护性

修改一个模块不会影响其他模块,降低维护成本。

下一步

基于 Apache License 2.0 许可发布