Skip to content

Application Structure

App Config

The app.Config struct is the central configuration for your application:

go
config := &app.Config{
    Service:   "user-service",
    Namespace: "my-namespace",
    Config:    &MyConfig{},
    
    // HTTP route registration
    Router: setupHTTPRoutes,
    
    // gRPC service registration
    GRPCRegister: setupGRPCServer,
    
    // Initialization functions
    InitFunc: []func() error{
        initDatabase,
        initCache,
        initMessageQueue,
    },
    
    // Teardown functions
    TeardownFunc: []func() error{
        closeDatabase,
        closeCache,
    },
}

Custom Configuration

Define a custom configuration struct that will be populated from your YAML config:

go
type MyConfig struct {
    APIKey     string `yaml:"api_key"`
    MaxRetries int    `yaml:"max_retries"`
}

func (c *MyConfig) Print() {
    // Implement configuration printing logic
}

Route Setup

go
func setupHTTPRoutes(r *gin.Engine) {
    // API route group
    api := r.Group("/api/v1")
    {
        api.GET("/users", listUsers)
        api.GET("/users/:id", getUser)
        api.POST("/users", createUser)
        api.PUT("/users/:id", updateUser)
        api.DELETE("/users/:id", deleteUser)
    }
    
    // Health checks
    r.GET("/health", healthCheck)
    r.GET("/ready", readinessCheck)
}

gRPC Setup

go
func setupGRPCServer(s *grpc.Server) {
    // Register gRPC services
    pb.RegisterUserServiceServer(s, &userServiceServer{})
}

Complete Example

go
package main

import (
    "context"
    "fmt"
    "net/http"
    "time"
    "log/slog"
    
    "butterfly.orx.me/core/app"
    "butterfly.orx.me/core/log"
    "butterfly.orx.me/core/store/gorm"
    "butterfly.orx.me/core/store/redis"
    "github.com/gin-gonic/gin"
    gormDriver "gorm.io/gorm"
)

var (
    db     *gormDriver.DB
    cache  *redis.Client
    logger = slog.With("service", "user-service")
)

type User struct {
    gormDriver.Model
    Name     string `json:"name" binding:"required"`
    Email    string `json:"email" binding:"required,email"`
    Password string `json:"-"`
}

type Config struct {
    JWTSecret string `yaml:"jwt_secret"`
    MaxUsers  int    `yaml:"max_users"`
}

func (c *Config) Print() {
    logger.Info("config loaded", 
        "max_users", c.MaxUsers,
    )
}

func main() {
    config := &app.Config{
        Service: "user-service",
        Config:  &Config{},
        Router:  setupRoutes,
        InitFunc: []func() error{
            initDB,
            initCache,
        },
    }
    
    app := app.New(config)
    app.Run()
}

func initDB() error {
    var err error
    db, err = gorm.NewDB("root:password@tcp(localhost:3306)/users?charset=utf8mb4")
    if err != nil {
        return fmt.Errorf("failed to connect database: %w", err)
    }
    return db.AutoMigrate(&User{})
}

func initCache() error {
    cache = redis.GetClient("cache")
    if cache == nil {
        logger.Warn("cache not configured")
    }
    return nil
}

func setupRoutes(r *gin.Engine) {
    r.GET("/health", func(c *gin.Context) {
        c.JSON(200, gin.H{"status": "healthy"})
    })
    
    api := r.Group("/api/v1")
    {
        api.GET("/users", listUsers)
        api.GET("/users/:id", getUser)
        api.POST("/users", createUser)
        api.PUT("/users/:id", updateUser)
        api.DELETE("/users/:id", deleteUser)
    }
}