Extending Built-in Modules
Extend and customize built-in EZ-Console modules.
Overview
EZ-Console provides a flexible architecture that allows you to extend built-in modules without modifying framework code. This includes extending controllers, services, models, and frontend components.
Extending Controllers
Wrap Built-in Controllers
package controller
import (
"context"
"github.com/gin-gonic/gin"
"github.com/sven-victor/ez-console/pkg/util"
"github.com/sven-victor/ez-console/server"
)
type ExtendedUserController struct {
baseController *BaseUserController
svc server.Service
}
func NewExtendedUserController(baseController *BaseUserController, svc server.Service) *ExtendedUserController {
return &ExtendedUserController{
baseController: baseController,
svc: svc,
}
}
// Extend base controller methods
func (c *ExtendedUserController) ListUsers(ctx *gin.Context) {
// Add custom logic before
// ...
// Call base controller
c.baseController.ListUsers(ctx)
// Add custom logic after
// ...
}
func (c *ExtendedUserController) RegisterRoutes(ctx context.Context, router *gin.RouterGroup) {
users := router.Group("/users")
{
users.GET("", c.ListUsers)
// Add additional routes
}
}
Override Routes
func (c *ExtendedUserController) RegisterRoutes(ctx context.Context, router *gin.RouterGroup) {
// Override default user routes
users := router.Group("/users")
{
users.GET("", c.ListUsers) // Custom implementation
users.GET("/:id", c.GetUser)
}
}
Extending Services
Wrapper Pattern
package service
import (
"context"
"github.com/sven-victor/ez-console/server"
)
type ExtendedUserService struct {
baseService server.Service
}
func NewExtendedUserService(baseService server.Service) *ExtendedUserService {
return &ExtendedUserService{baseService: baseService}
}
func (s *ExtendedUserService) CreateUser(ctx context.Context, user *User) error {
// Custom validation
if err := s.validateCustomRules(user); err != nil {
return err
}
// Call base service
err := s.baseService.CreateUser(ctx, user)
if err != nil {
return err
}
// Additional processing
s.sendWelcomeEmail(user)
return nil
}
Service Interface Extension
type ExtendedService interface {
server.Service
CustomMethod(ctx context.Context) error
}
type ExtendedServiceImpl struct {
server.Service
}
func (s *ExtendedServiceImpl) CustomMethod(ctx context.Context) error {
// Implementation
return nil
}
Extending Models
Model Composition
package model
type ExtendedProduct struct {
Product // Embed base model
CustomField string `gorm:"type:varchar(255)"`
}
func (p *ExtendedProduct) TableName() string {
return "t_product"
}
// Override GORM hooks
func (p *ExtendedProduct) BeforeCreate(tx *gorm.DB) error {
// Call base hook
p.Product.BeforeCreate(tx)
// Custom logic
p.CustomField = "default-value"
return nil
}
Custom Model Methods
func (p *ExtendedProduct) Validate() error {
// Custom validation
if p.Price < 0 {
return fmt.Errorf("price cannot be negative")
}
return nil
}
func (p *ExtendedProduct) CalculateTotal() float64 {
// Custom calculation
return p.Price * (1 + p.TaxRate)
}
Extending Frontend Components
Component Wrapping
// src/components/ExtendedUserList.tsx
import { UserList } from 'ez-console';
export const ExtendedUserList: React.FC = () => {
const customActions = (
<Button onClick={handleCustomAction}>
Custom Action
</Button>
);
return (
<UserList
extraActions={customActions}
onUserSelect={handleUserSelect}
/>
);
};
Route Extension
// src/App.tsx
import App from 'ez-console';
export default function MyApp() {
return (
<App
transformRouter={(routes) => {
// Modify existing routes
return routes.map(route => {
if (route.path === '/users') {
// Customize user route
return {
...route,
element: <ExtendedUserList />,
};
}
return route;
});
}}
/>
);
}
Custom Middleware
Extending Authentication
package middleware
import (
"github.com/gin-gonic/gin"
"github.com/sven-victor/ez-console/pkg/util"
)
func CustomAuthMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
// Custom authentication logic
token := c.GetHeader("X-Custom-Token")
if !validateCustomToken(token) {
util.RespondWithError(c, util.NewErrorMessage("E4011", "Invalid token"))
c.Abort()
return
}
// Add custom context
c.Set("custom_data", "value")
c.Next()
}
}
Configuration Extension
Custom Configuration
# config.yml
custom:
feature_enabled: true
custom_setting: "value"
Loading Custom Config
type CustomConfig struct {
FeatureEnabled bool `yaml:"feature_enabled"`
CustomSetting string `yaml:"custom_setting"`
}
var customConfig CustomConfig
func init() {
viper.UnmarshalKey("custom", &customConfig)
}
Best Practices
1. Composition over Modification
// ✅ Good: Compose existing functionality
type ExtendedService struct {
BaseService
// Additional fields
}
// ❌ Bad: Modify framework code
// Don't modify framework source directly
2. Use Interfaces
// ✅ Good: Use interfaces
type Service interface {
Method() error
}
// ❌ Bad: Concrete types
type Service struct {
// ...
}
3. Keep Extensions Separate
project/
├── extensions/
│ ├── controller/
│ ├── service/
│ └── model/
└── ...
4. Document Extensions
Document how extensions work and how they differ from base functionality.
Related Topics
- Hooks & Events — Lifecycle and event hooks
Need help? Ask in GitHub Discussions.