Framingo is a modular, service-oriented Go framework for building production-ready HTTP API applications. It provides a comprehensive set of tools, utilities, and architectural patterns to help you quickly develop scalable, maintainable backend services.
-
Service-Oriented Architecture - Built-in service lifecycle management with automatic dependency resolution
-
HTTP API Server - Production-ready API server with routing, middleware, and rate limiting
-
Rich Utilities - Extensive collection of utility packages for common operations
-
Modular Design - Pick and choose components based on your needs
-
Configuration Management - YAML-based configuration with environment variable overrides
-
Database Integration - Database manager with connection pooling and migrations
-
CLI Support - Built-in command-line interface framework integration
-
Production Ready - Error handling, logging, graceful shutdown, and profiling support
- Quick Start
- Architecture
- Core Modules
- Building Your First Application
- Documentation
- Examples
- Contributing
- License
go get github.com/xhanio/framingoCreate a simple HTTP API server in minutes:
// cmd/myapp/main.go
package main
import (
"fmt"
"os"
"github.com/xhanio/framingo/example/pkg/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)
}
}# config.yaml
log:
level: 0 # 0=Debug, 1=Info, 2=Warn, 3=Error
api:
http:
host: 0.0.0.0
port: 8080
prefix: /api/v1# Build and run
go build -o myapp cmd/myapp/main.go
./myapp daemon -c config.yaml
# Test the API
curl -X GET "http://localhost:8080/api/v1/demo/example?message=Hello"
# Response: {"id":1,"message":"Hello","created_at":"...","updated_at":"..."}For a complete tutorial, see the Building Your First Application section and Complete Application Guide.
Framingo follows a layered architecture pattern:
graph TB
subgraph "Application Layer"
CLI[CLI Interface]
Config[Configuration]
end
subgraph "Service Orchestration"
Controller[Service Controller<br/>Lifecycle & Dependencies]
end
subgraph "Core Components"
Services[Services<br/>Business Logic]
API[API Layer<br/>HTTP Server]
Utils[Utilities<br/>Helper Functions]
end
CLI --> Controller
Config --> Controller
Controller --> Services
Controller --> API
Controller --> Utils
style CLI fill:#e1f5ff
style Config fill:#e1f5ff
style Controller fill:#fff4e1
style Services fill:#f0f0f0
style API fill:#f0f0f0
style Utils fill:#f0f0f0
sequenceDiagram
participant Client
participant APIServer as API Server
participant Middleware as Middleware Pipeline
participant Router as Router/Handler
participant Service as Service Layer
participant DB as Database/External Services
Client->>APIServer: HTTP Request
APIServer->>Middleware: Process Request
Note over Middleware: Authentication<br/>Logging<br/>Rate Limiting
Middleware->>Router: Validated Request
Router->>Service: Business Operation
Service->>DB: Data Access
DB-->>Service: Data
Service-->>Router: Result
Router-->>Middleware: Response
Middleware-->>APIServer: Formatted Response
APIServer-->>Client: HTTP Response
Framingo is organized into four core module categories under pkg/:
Production-ready service implementations that provide core functionality:
-
api/server - HTTP API server with Echo framework integration
- Route management and handler registration
- Built-in middleware (error handling, logging, rate limiting)
- Multiple server support with TLS
- YAML-based route configuration
- Request/response lifecycle management
-
api/client - HTTP client utilities for making API requests
-
controller - Service lifecycle orchestration
- Automatic dependency resolution via topological sort
- Service registration and initialization
- Graceful startup and shutdown
- Signal handling
-
db - Database manager with GORM integration
- Connection pooling with configurable limits
- Migration support
- Multiple database types (PostgreSQL, MySQL, SQLite)
- Context-aware operations
-
planner - Task scheduling and planning service
-
pubsub - Publish-subscribe messaging pattern implementation
Core interfaces and type definitions used throughout the framework:
-
common - Common interfaces
Service- Base service interface with name and dependenciesDaemon- Start/Stop lifecycle methodsInitializable- Initialization interfaceNamed,Unique,Weighted- Utility interfaces
-
api - HTTP API related types
Router,Middleware,Handlerinterfaces- Request/Response types
- Error categories
-
info - Application metadata (version, build info, commit hash)
Efficient, generic data structures for common algorithms:
- buffer - Ring buffer implementation with fixed capacity
- graph - Generic graph structure with topological sort (used by controller)
- lease - Time-based lease management for resource allocation
- queue - FIFO queue implementation
- staque - Hybrid stack/queue data structure
- trie - Prefix tree for efficient string matching
Helper packages for common tasks:
- certutil - Certificate and TLS utilities
- cmdutil - Command execution helpers
- infra - Infrastructure helpers (signals, profiling, pprof)
- ioutil - I/O operations and file utilities
- job - Background job execution framework
- log - Structured logging with zap integration and file rotation
- maputil - Map and set utilities
- netutil - Network utilities
- pathutil - Path manipulation helpers
- printutil - Pretty printing utilities
- reflectutil - Reflection helpers
- sliceutil - Slice manipulation utilities
- strutil - String utilities
- task - Task management utilities
- timeutil - Time-related utilities
# Create your project structure
mkdir -p myapp/{cmd/myapp,pkg/{services,routers,middlewares,types/{api,entity,orm},components/{cmd,server},utils}}
cd myapp
# Initialize Go module
go mod init github.com/yourorg/myapp
# Get Framingo
go get github.com/xhanio/framingo
# Your project structure should look like:
# myapp/
# ├── cmd/
# │ └── myapp/ # Application entry point
# ├── pkg/
# │ ├── components/
# │ │ ├── cmd/ # CLI commands
# │ │ └── server/ # Server orchestration
# │ ├── services/ # Business logic
# │ ├── routers/ # HTTP handlers
# │ ├── middlewares/ # HTTP middleware
# │ ├── types/ # Type definitions
# │ │ ├── api/ # API request/response types with validation
# │ │ ├── entity/ # Business entities (pure domain models)
# │ │ └── orm/ # ORM models for database operations
# │ └── utils/ # Utilities
# └── config.yaml # ConfigurationServices contain your business logic with lifecycle management.
// pkg/services/hello/model.go
package hello
import (
"context"
"github.com/xhanio/framingo/pkg/types/common"
)
type Manager interface {
common.Service
common.Initializable
common.Daemon
SayHello(ctx context.Context, name string) (string, error)
}// pkg/services/hello/manager.go
package hello
import (
"context"
"fmt"
"github.com/xhanio/framingo/pkg/types/common"
"github.com/xhanio/framingo/pkg/utils/log"
)
type manager struct {
log log.Logger
}
// Option pattern for optional dependencies
type Option func(*manager)
func WithLogger(logger log.Logger) Option {
return func(m *manager) { m.log = logger }
}
func New(opts ...Option) Manager {
m := &manager{}
for _, opt := range opts {
opt(m)
}
if m.log == nil {
m.log = log.Default
}
return m
}
func (m *manager) Name() string { return "hello-service" }
func (m *manager) Dependencies() []common.Service { return nil }
func (m *manager) Init() error {
m.log.Info("Initializing hello service")
return nil
}
func (m *manager) Start(ctx context.Context) error {
m.log.Info("Starting hello service")
return nil
}
func (m *manager) Stop(wait bool) error {
m.log.Info("Stopping hello service")
return nil
}
func (m *manager) SayHello(ctx context.Context, name string) (string, error) {
m.log.Infof("Saying hello to %s", name)
return fmt.Sprintf("Hello, %s!", name), nil
}Routers define HTTP endpoints and handlers using YAML configuration.
// pkg/routers/hello/router.go
package hello
import (
_ "embed"
"github.com/labstack/echo/v4"
"github.com/xhanio/framingo/pkg/types/api"
"github.com/xhanio/framingo/pkg/types/common"
"myapp/pkg/services/hello"
)
//go:embed router.yaml
var config []byte
type router struct {
helloSvc hello.Manager
}
func New(svc hello.Manager) api.Router {
return &router{helloSvc: svc}
}
func (r *router) Name() string { return "hello-router" }
func (r *router) Dependencies() []common.Service { return []common.Service{r.helloSvc} }
func (r *router) Config() []byte { return config }
func (r *router) Handlers() map[string]echo.HandlerFunc {
return map[string]echo.HandlerFunc{
"Hello": r.Hello,
}
}
func (r *router) Hello(c echo.Context) error {
name := c.QueryParam("name")
if name == "" {
name = "World"
}
msg, err := r.helloSvc.SayHello(c.Request().Context(), name)
if err != nil {
return err
}
return c.JSON(200, map[string]string{"message": msg})
}# pkg/routers/hello/router.yaml
server: http
prefix: /hello
handlers:
- method: GET
path: /
func: HelloCreate a server component to orchestrate your services and routers.
// pkg/components/server/myapp/manager.go
package myapp
import (
"context"
"fmt"
"os"
"github.com/spf13/viper"
"github.com/xhanio/framingo/pkg/services/api/server"
"github.com/xhanio/framingo/pkg/services/controller"
"github.com/xhanio/framingo/pkg/types/common"
"github.com/xhanio/framingo/pkg/utils/infra"
"github.com/xhanio/framingo/pkg/utils/log"
"myapp/pkg/services/hello"
helloRouter "myapp/pkg/routers/hello"
)
type Manager interface {
common.Daemon
}
type manager struct {
log log.Logger
services *controller.Manager
api server.Manager
helloSvc hello.Manager
}
func New() Manager {
return &manager{}
}
func (m *manager) Init() error {
// Initialize logger
m.log = log.New(log.WithLevel(viper.GetInt("log.level")))
// Initialize service controller
m.services = controller.New(controller.WithLogger(m.log))
// Create API server
m.api = server.New(server.WithLogger(m.log))
httpConfig := viper.Sub("api.http")
if httpConfig != nil {
m.api.Add("http",
server.WithEndpoint(
httpConfig.GetString("host"),
httpConfig.GetInt("port"),
httpConfig.GetString("prefix"),
),
)
}
// Initialize your service (with optional logger)
m.helloSvc = hello.New(hello.WithLogger(m.log))
// Register services
m.services.Register(m.helloSvc)
// Resolve dependencies
if err := m.services.TopoSort(); err != nil {
return err
}
// Add API server (must be last)
m.services.Register(m.api)
// Initialize all services
if err := m.services.Init(); err != nil {
return err
}
// Register routers
return m.api.RegisterRouters(helloRouter.New(m.helloSvc))
}
func (m *manager) Start(ctx context.Context) error {
// Handle signals
go infra.HandleSignals(m.log, m.services)
// Start all services
return m.services.Start(ctx)
}
func (m *manager) Stop(wait bool) error {
return m.services.Stop(wait)
}// cmd/myapp/main.go
package main
import (
"fmt"
"os"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"myapp/pkg/components/server/myapp"
)
func main() {
var configFile string
rootCmd := &cobra.Command{Use: "myapp"}
daemonCmd := &cobra.Command{
Use: "daemon",
RunE: func(cmd *cobra.Command, args []string) error {
// Load configuration
viper.SetConfigFile(configFile)
viper.SetEnvPrefix("MYAPP")
viper.AutomaticEnv()
if err := viper.ReadInConfig(); err != nil {
return fmt.Errorf("failed to read config: %w", err)
}
// Initialize and start server
mgr := myapp.New()
if err := mgr.Init(); err != nil {
return err
}
return mgr.Start(cmd.Context())
},
}
daemonCmd.Flags().StringVarP(&configFile, "config", "c", "config.yaml", "config file path")
rootCmd.AddCommand(daemonCmd)
if err := rootCmd.Execute(); err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
}
}# Build your application
go build -o myapp cmd/myapp/main.go
# Run the server
./myapp daemon -c config.yaml
# Test the endpoint
curl http://localhost:8080/api/v1/hello?name=Framingo
# Response: {"message": "Hello, Framingo!"}For more details, see the Complete Application Guide with comprehensive examples of all components.
- Complete Application Guide - Full tutorial with all components and production deployment
- Type Separation Guide - Understanding API, Entity, and ORM type separation
- Service Layer - Building business logic services with lifecycle management
- HTTP Routers - Creating API endpoints with YAML-based configuration
- Middleware - Request processing pipeline and custom middleware
- Server Component - Application orchestration and dependency management
- CLI Component - Command-line interface with Cobra
Browse the framework source code:
- Services API - API server, controller, database, pubsub
- Types - Common interfaces and API types
- Utilities - Logger, infrastructure, and helper packages
- Data Structures - Graph, queue, buffer, trie implementations
View package documentation using go doc:
# View package documentation
go doc github.com/xhanio/framingo/pkg/services/api/server
go doc github.com/xhanio/framingo/pkg/services/controller
go doc github.com/xhanio/framingo/pkg/utils/logThe example/ directory contains a complete, production-ready application demonstrating all framework features:
example/
+-- pkg/
+-- components/
| +-- cmd/example/ # CLI with Cobra (daemon, version commands)
| +-- server/example/ # Server orchestration and lifecycle management
+-- services/example/ # Business service with lifecycle methods
+-- routers/example/ # HTTP routes with YAML configuration
+-- middlewares/example/ # Custom middleware (deflate compression)
+-- types/ # Type definitions (separated by purpose)
| +-- api/ # API request/response types with validation
| +-- entity/ # Business entities (pure domain models)
| +-- orm/ # ORM models for database operations
+-- utils/ # Utility modules
+-- infra/ # Infrastructure utilities
+-- README.md # Complete application guide
# Clone the repository
git clone https://github.com/xhanio/framingo.git
cd framingo
# Create a config file
cat > config.yaml <<EOF
log:
level: 0
api:
http:
host: 0.0.0.0
port: 8080
prefix: /api/v1
EOF
# Create a main.go that uses the example components
cat > cmd/example/main.go <<EOF
package main
import (
"fmt"
"os"
"github.com/xhanio/framingo/example/pkg/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)
}
}
EOF
# Build and run
go build -o myapp cmd/example/main.go
./myapp daemon -c config.yaml
# Test the API
curl -X GET "http://localhost:8080/api/v1/demo/example?message=Hello%20World"
# Response: {"id":1,"message":"Hello World","created_at":"...","updated_at":"..."}For detailed setup and configuration, see example/README.md
- ✅ Service lifecycle management with dependency injection
- ✅ HTTP API with routing and request validation
- ✅ Type separation (API/Entity/ORM) for clean architecture
- ✅ Database integration with ORM models
- ✅ Custom middleware (deflate compression)
- ✅ YAML-based configuration with environment overrides
- ✅ CLI with multiple commands (daemon, version)
- ✅ Graceful shutdown and signal handling
- ✅ Structured logging with file rotation
- ✅ Service dependencies with automatic resolution
- ✅ Multiple API servers support
Framingo promotes clean architecture through type separation:
// API Types - Request/response with validation
type CreateUserRequest struct {
Username string `json:"username" validate:"required"`
Email string `json:"email" validate:"required,email"`
}
// Entity Types - Pure business models
type User struct {
ID int64 `json:"id"`
Username string `json:"username"`
Email string `json:"email"`
}
// ORM Types - Database models
type User struct {
ID int64 `gorm:"primaryKey"`
Username string `gorm:"type:varchar(100);not null"`
Email string `gorm:"type:varchar(255);not null"`
}
func (User) TableName() string { return "users" }Benefits:
- API contracts independent of storage
- Business logic isolated from infrastructure
- Easy to test and maintain
- Flexibility to change database without affecting API
All services implement a standard lifecycle:
type Service interface {
Name() string
Dependencies() []common.Service
}
type Initializable interface {
Init() error
}
type Daemon interface {
Start(context.Context) error
Stop(wait bool) error
}Services declare their dependencies via constructor arguments (required) and options (optional):
// Required dependencies as constructor arguments
func New(database db.Manager, opts ...Option) Manager {
m := &manager{db: database}
for _, opt := range opts {
opt(m)
}
return m
}
// Service declares dependencies for lifecycle management
func (s *myService) Dependencies() []common.Service {
return []common.Service{s.database}
}
// Controller automatically starts: database -> myServiceRouters use YAML configuration for declarative route definition:
server: http
prefix: /api/users
handlers:
- method: GET
path: /:id
func: GetUser
- method: POST
path: /
func: CreateUser
middleware: authMiddlewares process requests in order:
Request -> Recover -> Info -> Throttle -> Logger -> Custom -> Handler -> Response
Framingo uses Viper for configuration management with priority:
- Command-line flags (highest)
- Environment variables
- YAML configuration file
- Default values (lowest)
# config.yaml
log:
level: 0 # 0=Debug, 1=Info, 2=Warn, 3=Error
file: /var/log/app.log
db:
type: postgres
source:
host: localhost
port: 5432
api:
http:
host: 0.0.0.0
port: 8080
prefix: /api/v1
throttle:
rps: 100.0
burst_size: 200Environment variable override:
export FRAMINGO_API_HTTP_PORT=9090FROM golang:1.21 AS builder
WORKDIR /app
COPY . .
RUN go build -o app cmd/app/main.go
FROM alpine:latest
COPY --from=builder /app/app /usr/local/bin/
COPY config.yaml /etc/app/
CMD ["app", "daemon", "-c", "/etc/app/config.yaml"]apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
replicas: 3
template:
spec:
containers:
- name: myapp
image: myapp:latest
ports:
- containerPort: 8080[Unit]
Description=My Framingo App
[Service]
ExecStart=/usr/local/bin/myapp daemon -c /etc/myapp/config.yaml
Restart=on-failure
[Install]
WantedBy=multi-user.target-
Architecture & Types
- Separate types by purpose:
api/for requests,entity/for business,orm/for database - Pass required dependencies as constructor arguments, not options
- Keep business logic independent of infrastructure concerns
- Use interfaces for testability and flexibility
- Separate types by purpose:
-
Service Design
- Keep services focused and single-purpose
- Declare dependencies explicitly in
Dependencies()method - Implement standard lifecycle methods (
Init,Start,Stop) - Convert between ORM and entity types in service layer
-
Error Handling
- Use error wrapping for context (
errors.Wrap) - Return appropriate HTTP status codes (BadRequest, NotFound, etc.)
- Log errors at service boundaries
- Validate requests using API types with validation tags
- Use error wrapping for context (
-
Configuration
- Use YAML for structure and hierarchy
- Use env vars for secrets and environment-specific values
- Validate configuration on startup
- Follow configuration priority: flags > env vars > file > defaults
-
Testing
- Test services independently with mocked dependencies
- Test handlers with API type validation
- Integration tests for full request flow
- Test ORM to entity conversions
-
Performance
- Enable database connection pooling
- Configure rate limiting per endpoint or server-wide
- Use pprof for profiling bottlenecks
- Cache frequently accessed data when appropriate
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is licensed under the Apache License 2.0 - see the LICENSE file for details.
- Complete Application Guide - Full tutorial and production deployment guide
- Example Components - Reference implementations of all components
- Issue Tracker - Bug reports and feature requests
- Discussions - Questions and community support
Built with:
- Echo - High performance HTTP framework
- Cobra - CLI framework
- Viper - Configuration management
- GORM - ORM library
Start building with Framingo today!