example/

directory
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 6, 2025 License: Apache-2.0

README

Building Applications with Framingo

This directory contains a complete example application demonstrating how to build production-ready HTTP API services using the Framingo framework. This guide walks you through all components and shows how they work together.

Table of Contents

Overview

The Framingo framework provides a modular, service-oriented architecture for building scalable HTTP APIs. This example demonstrates:

  • Service Layer: Business logic with lifecycle management
  • HTTP API Layer: RESTful endpoints with routing and middleware
  • Command Layer: CLI interface for application control
  • Configuration Management: YAML-based config with environment variable overrides
  • Dependency Management: Automatic service dependency resolution

Project Structure

example/
 components/          # Application components
    cmd/            # CLI interface (Cobra)
       example/    # Command implementations
    server/         # Server component
        example/    # Server orchestration
 services/           # Business logic services
    example/        # Example service implementation
 routers/            # HTTP route handlers
    example/        # Example router with handlers
 middlewares/        # HTTP middleware
    example/        # Example middleware (deflate compression)
 types/              # Type definitions
    entity/         # Business entities
    infra/          # Infrastructure types
 README.md           # This file

Quick Start

1. Create Your Main Application
// cmd/myapp/main.go
package main

import (
    "fmt"
    "os"

    "github.com/xhanio/framingo/example/components/cmd/example"
)

func main() {
    rootCmd := example.NewRootCmd()
    if err := rootCmd.Execute(); err != nil {
        fmt.Fprintf(os.Stderr, "Error: %v\n", err)
        os.Exit(1)
    }
}
2. Create Configuration File
# config.yaml
log:
  level: 0  # Debug
  file: /var/log/myapp.log
  rotation:
    max_size: 100
    max_backups: 3
    max_age: 7

db:
  type: postgres
  source:
    host: 127.0.0.1
    port: 5432
    user: dbuser
    password: dbpass
    dbname: mydb
  connection:
    max_open: 10
    max_idle: 5
    max_lifetime: 1h

api:
  http:
    host: 0.0.0.0
    port: 8080
    prefix: /api/v1
    throttle:
      rps: 100.0
      burst_size: 200
3. Build and Run
# Build the application
go build -o myapp cmd/myapp/main.go

# Run the server
./myapp daemon -c config.yaml

# Check version
./myapp version
4. Test the API
# Test the example endpoint
curl http://localhost:8080/api/v1/demo/example
# Response: Good

Architecture

Layer Overview

                        CLI Layer                             
  (components/cmd) - User interface, argument parsing         
,
                       
�
                   Server Component                           
  (components/server) - Orchestration, lifecycle management   
,
                       
       <
                                     
� � �
  Services      Routers    Middlewares 
  (business)    (HTTP)      (HTTP)     
  
Request Flow
HTTP Request
    
    �

  API Server (pkg/services/api/server)   
  - Route matching                       
  - Built-in middleware chain            
,
               
               �

  Custom Middleware (middlewares/)       
  - Request preprocessing                
  - Authentication, validation, etc.     
,
               
               �

  Router Handler (routers/)              
  - Request parsing                      
  - Call service layer                   
,
               
               �

  Service (services/)                    
  - Business logic                       
  - Data access                          
,
               
               �
         HTTP Response

Building Your Application

Step 1: Define Your Entities

Create entity types for your domain models:

// types/entity/user.go
package entity

type User struct {
    ID       string `json:"id"`
    Username string `json:"username"`
    Email    string `json:"email"`
}

See types/entity/ for examples.

Step 2: Create a Service

Services contain your business logic and implement lifecycle management.

// services/user/model.go
package user

import (
    "context"
    "github.com/xhanio/framingo/pkg/types/common"
    "github.com/xhanio/framingo/example/types/entity"
)

type Manager interface {
    common.Service
    common.Initializable
    common.Daemon
    GetUser(ctx context.Context, id string) (*entity.User, error)
    CreateUser(ctx context.Context, user *entity.User) error
}
// services/user/manager.go
package user

type manager struct {
    log log.Logger
    db  db.Manager
}

func New(db db.Manager, logger log.Logger) Manager {
    return &manager{
        log: logger,
        db:  db,
    }
}

func (m *manager) GetUser(ctx context.Context, id string) (*entity.User, error) {
    // Business logic here
    return &entity.User{ID: id}, nil
}

=� Learn more: Service Documentation

Step 3: Create a Router

Routers define HTTP endpoints and handlers.

// routers/user/router.yaml
server: http
prefix: /users
handlers:
  - method: GET
    path: /:id
    func: GetUser
  - method: POST
    path: /
    func: CreateUser
// routers/user/handler.go
package user

import (
    "github.com/labstack/echo/v4"
    "github.com/xhanio/framingo/example/services/user"
)

type router struct {
    userService user.Manager
}

func (r *router) GetUser(c echo.Context) error {
    id := c.Param("id")
    user, err := r.userService.GetUser(c.Request().Context(), id)
    if err != nil {
        return err
    }
    return c.JSON(200, user)
}

=� Learn more: Router Documentation

Step 4: Create Middleware (Optional)

Middlewares process requests before they reach handlers.

// middlewares/auth/middleware.go
package auth

func (m *middleware) Func(next echo.HandlerFunc) echo.HandlerFunc {
    return func(c echo.Context) error {
        token := c.Request().Header.Get("Authorization")
        if !m.validateToken(token) {
            return errors.Unauthorized.New("invalid token")
        }
        return next(c)
    }
}

=� Learn more: Middleware Documentation

Step 5: Wire Everything Together

Update the server component to register your services, routers, and middlewares.

// components/server/example/api.go
func (m *manager) initAPI() error {
    middlewares := []api.Middleware{
        authmw.New(),
    }

    routers := []api.Router{
        user.New(m.userService, m.log),
    }

    m.api.RegisterMiddlewares(middlewares...)
    return m.api.RegisterRouters(routers...)
}
// components/server/example/manager.go - Init() method
func (m *manager) Init() error {
    // ... (logger, db setup)

    // Initialize your services
    m.userService = user.New(m.db, m.log)

    // Register services
    m.services.Register(
        m.db,
        m.userService,
    )

    // Resolve dependencies
    if err := m.services.TopoSort(); err != nil {
        return errors.Wrap(err)
    }

    // Add API server (must be last)
    m.services.Register(m.api)

    // Initialize all services
    if err := m.services.Init(); err != nil {
        return err
    }

    // Initialize API components
    return m.initAPI()
}

=� Learn more: Server Component Documentation

Step 6: Build and Deploy
# Build with version info
go build -ldflags="-X github.com/xhanio/framingo/pkg/types/info.Version=1.0.0" \
         -o myapp cmd/myapp/main.go

# Run
./myapp daemon -c config.yaml

=� Learn more: CLI Documentation

Component Reference

Services Layer

Purpose: Implement business logic with lifecycle management

Key Features:

  • Dependency injection
  • Lifecycle methods (Init, Start, Stop)
  • Context-aware operations
  • Automatic dependency resolution

Documentation: services/README.md

Example: services/example/

Routers Layer

Purpose: Define HTTP endpoints and handle requests

Key Features:

  • YAML-based route configuration
  • Automatic handler discovery via reflection
  • Service dependency injection
  • Echo framework integration

Documentation: routers/README.md

Example: routers/example/

Middlewares Layer

Purpose: Process HTTP requests before handlers

Key Features:

  • Request/response modification
  • Authentication and authorization
  • Rate limiting (built-in)
  • Error handling (built-in)

Documentation: middlewares/README.md

Example: middlewares/example/

Server Component

Purpose: Orchestrate all services and manage lifecycle

Key Features:

  • Configuration management (Viper)
  • Service dependency resolution
  • Multiple API servers
  • Graceful shutdown
  • Signal handling
  • pprof profiling

Documentation: components/server/README.md

Example: components/server/example/

CMD Component

Purpose: Provide CLI interface for the application

Key Features:

  • Command-line argument parsing (Cobra)
  • Multiple commands support
  • Help generation
  • Version information

Documentation: components/cmd/README.md

Example: components/cmd/example/

Development Workflow

Project Setup
  1. Clone or create your project structure:
mkdir -p myapp/{cmd/myapp,services,routers,middlewares,types/entity,components/{cmd,server}}
  1. Initialize Go module:
cd myapp
go mod init github.com/yourorg/myapp
go get github.com/xhanio/framingo
  1. Copy example files as templates:
# Use example files as starting point
cp -r $FRAMINGO_PATH/example/services/example myapp/services/myservice
# Modify as needed
Development Cycle
  1. Define your entities (types/entity/)
  2. Implement services (services/)
  3. Create routers (routers/)
  4. Add middlewares if needed (middlewares/)
  5. Wire in server component (components/server/)
  6. Test locally:
go run cmd/myapp/main.go daemon -c config.yaml
Testing
# Run tests
go test ./...

# Run with coverage
go test -cover ./...

# Run specific service tests
go test ./services/myservice/...
Debugging

Using pprof:

# config.yaml
pprof:
  port: 6060
# Start server
./myapp daemon -c config.yaml

# Access profiling
go tool pprof http://localhost:6060/debug/pprof/profile

Using signals:

# Print service info
kill -USR1 $(pgrep myapp)

# Print stack traces
kill -USR2 $(pgrep myapp)

# Graceful shutdown
kill -INT $(pgrep myapp)

Production Deployment

Build for Production
#!/bin/bash
VERSION=$(git describe --tags --always)
COMMIT=$(git rev-parse HEAD)
BUILD_TIME=$(date -u +%Y-%m-%dT%H:%M:%SZ)

go build -ldflags=" \
  -X github.com/xhanio/framingo/pkg/types/info.Version=${VERSION} \
  -X github.com/xhanio/framingo/pkg/types/info.Commit=${COMMIT} \
  -X github.com/xhanio/framingo/pkg/types/info.BuildTime=${BUILD_TIME}" \
  -o myapp cmd/myapp/main.go
Docker Deployment
# Dockerfile
FROM golang:1.21 AS builder
WORKDIR /app
COPY go.* ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o myapp cmd/myapp/main.go

FROM alpine:latest
RUN apk --no-cache add ca-certificates tzdata
WORKDIR /root/
COPY --from=builder /app/myapp .
COPY config.yaml .
EXPOSE 8080
CMD ["./myapp", "daemon", "-c", "config.yaml"]
# Build and run
docker build -t myapp:latest .
docker run -p 8080:8080 myapp:latest
Kubernetes Deployment
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
spec:
  replicas: 3
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
      - name: myapp
        image: myapp:latest
        ports:
        - containerPort: 8080
        env:
        - name: FRAMINGO_DB_HOST
          valueFrom:
            secretKeyRef:
              name: db-secret
              key: host
        volumeMounts:
        - name: config
          mountPath: /etc/myapp
      volumes:
      - name: config
        configMap:
          name: myapp-config
Systemd Service
# /etc/systemd/system/myapp.service
[Unit]
Description=My Framingo Application
After=network.target postgresql.service

[Service]
Type=simple
User=myapp
Group=myapp
WorkingDirectory=/opt/myapp
ExecStart=/opt/myapp/bin/myapp daemon -c /etc/myapp/config.yaml
Restart=on-failure
RestartSec=5s
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target
Configuration Management

Environment-specific configs:

# Development
./myapp daemon -c config.dev.yaml

# Staging
./myapp daemon -c config.staging.yaml

# Production
./myapp daemon -c config.prod.yaml

Environment variable overrides:

export FRAMINGO_DB_HOST=prod-db.example.com
export FRAMINGO_DB_PASSWORD=$(cat /run/secrets/db_password)
export FRAMINGO_API_HTTP_PORT=8080
./myapp daemon -c config.yaml

Best Practices

Project Organization
  1. Separate concerns: Keep services, routers, and middlewares in separate packages
  2. Use entity types: Define clear data structures in types/entity
  3. Dependency injection: Pass dependencies explicitly, don't use globals
  4. Interface-based design: Define interfaces in model.go files
Configuration
  1. Use YAML for structure: Define server config, DB settings in YAML
  2. Use env vars for secrets: Override sensitive data via environment variables
  3. Validate on startup: Check configuration during Init() phase
  4. Document defaults: Provide sensible defaults for all config values
Error Handling
  1. Use error wrapping: Use errors.Wrap() to add context
  2. Return appropriate HTTP codes: Use error categories (BadRequest, NotFound, etc.)
  3. Log errors: Always log errors at service boundaries
  4. Don't panic: Use error returns instead of panics
Testing
  1. Test services independently: Mock dependencies for unit tests
  2. Test handlers with contexts: Use echo test utilities
  3. Integration tests: Test full request flow end-to-end
  4. Coverage: Aim for >80% coverage on critical paths
Performance
  1. Use connection pooling: Configure DB connection limits
  2. Enable throttling: Set rate limits per endpoint or server-wide
  3. Profile regularly: Use pprof to identify bottlenecks
  4. Cache when appropriate: Add caching layer for expensive operations

Common Patterns

Adding Database Support
// In service
type manager struct {
    db db.Manager
}

func (m *manager) GetUser(ctx context.Context, id string) (*entity.User, error) {
    var user entity.User
    err := m.db.DB().WithContext(ctx).
        Table("users").
        Where("id = ?", id).
        First(&user).Error
    return &user, errors.Wrap(err)
}
Adding Authentication Middleware
// middlewares/auth/middleware.go
func (m *middleware) Func(next echo.HandlerFunc) echo.HandlerFunc {
    return func(c echo.Context) error {
        token := c.Request().Header.Get("Authorization")
        user, err := m.validateToken(token)
        if err != nil {
            return errors.Unauthorized.New("invalid token")
        }
        c.Set("user", user)
        return next(c)
    }
}

// In handler
func (r *router) GetProfile(c echo.Context) error {
    user := c.Get("user").(*entity.User)
    return c.JSON(200, user)
}
Multiple API Servers
# config.yaml
api:
  public:
    host: 0.0.0.0
    port: 8080
    prefix: /api/v1
  admin:
    host: 127.0.0.1
    port: 8081
    prefix: /admin
# routers/public/router.yaml
server: public  # Target public server

# routers/admin/router.yaml
server: admin   # Target admin server

Troubleshooting

Service won't start
  1. Check config file path: ./myapp daemon -c /correct/path/config.yaml
  2. Check configuration syntax: cat config.yaml | yaml-lint
  3. Check logs for initialization errors
  4. Verify database connectivity
Handler not found (404)
  1. Check router YAML configuration (server, prefix, path)
  2. Verify handler function name matches YAML
  3. Check router registration in api.go
  4. Enable debug logging to see registered routes
Service dependencies error
  1. Check Dependencies() method returns correct services
  2. Ensure services are registered before TopoSort()
  3. Look for circular dependencies
  4. Verify all required services are initialized
Rate limiting issues
  1. Check throttle configuration in config.yaml
  2. Adjust RPS and burst_size for your load
  3. Consider per-handler throttle overrides
  4. Monitor rate limit errors in logs

Resources

Framework Documentation
External Libraries
Example Components

Next Steps

  1. Explore the examples: Review each component's README and implementation
  2. Create a simple service: Start with a basic service following the example pattern
  3. Add an API endpoint: Create a router with handlers for your service
  4. Test locally: Build and run your application
  5. Add features incrementally: Middleware, database integration, etc.
  6. Deploy: Follow production deployment guidelines

Getting Help

  • Check component-specific README files for detailed documentation
  • Review example implementations in each directory
  • Examine the framework source code in pkg/ directory
  • Look at integration patterns in components/server/example/

Happy Building with Framingo! =�

Directories

Path Synopsis
components
middlewares
routers
services
types

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL