Architecture
Understand how Forge components work together
This page explains how Forge's core components are structured, how they interact, and the flow a request takes through the system.
High-Level Overview
A Forge application is composed of several interconnected components, all owned and orchestrated by the App instance:
Core Components
App
The App is the root object. It holds references to every other component and manages the application lifecycle. You create it with forge.New() or forge.NewApp() and interact with it through the App interface:
type App interface {
// Core components
Container() Container
Router() Router
Config() ConfigManager
Logger() Logger
Metrics() Metrics
HealthManager() HealthManager
LifecycleManager() LifecycleManager
// Lifecycle
Start(ctx context.Context) error
Stop(ctx context.Context) error
Run() error
// Registration
RegisterService(name string, factory Factory, opts ...RegisterOption) error
RegisterController(controller Controller) error
RegisterExtension(ext Extension) error
// Hooks
RegisterHook(phase LifecyclePhase, hook LifecycleHook, opts LifecycleHookOptions) error
RegisterHookFn(phase LifecyclePhase, name string, hook LifecycleHook) error
// Information
Name() string
Version() string
Environment() string
StartTime() time.Time
Uptime() time.Duration
Extensions() []Extension
GetExtension(name string) (Extension, error)
}Container (Vessel)
The dependency injection container manages service lifetimes. Forge uses Vessel as its DI engine, exposed through the Container type alias.
Services are registered with one of three lifetimes:
| Lifetime | Behavior |
|---|---|
| Singleton | Created once, shared across the entire application |
| Transient | Created fresh on every Resolve call |
| Scoped | Created once per scope (typically per HTTP request) |
The container automatically resolves dependency chains, detects circular references, and manages startup/shutdown ordering.
Router
The router handles HTTP request dispatch, middleware execution, and supports multiple protocols:
- Standard HTTP methods:
GET,POST,PUT,DELETE,PATCH,OPTIONS,HEAD,Any - Streaming:
WebSocket,SSE,EventStream - Advanced:
WebTransport,Handle(mount rawhttp.Handler) - Organization:
Groupfor path prefixes,Use/UseGlobalfor middleware
Routes are defined with the handler signature func(ctx forge.Context) error. Returning an error from a handler automatically produces an appropriate HTTP error response.
ConfigManager
The configuration system (powered by confy) provides:
- Auto-discovery of
config.yamlandconfig.local.yaml - Environment variable overrides with configurable prefix and separator
- Bind config sections to Go structs with
Bind(key, &target) - Type-safe accessors:
Get,GetString,GetInt,GetBool
Logger
Structured, leveled logging with multiple output formats:
- Development: Colorized, human-readable output (default in
developmentenvironment) - Production: JSON-formatted structured logs (default in
productionenvironment) - Noop: Silent logger for other environments
Log fields are added with forge.F(key, value).
Metrics
Collects counters, gauges, and histograms. Exports in Prometheus format at /_/metrics. Supports system metrics, runtime metrics, and HTTP request metrics.
HealthManager
Manages health check registration and aggregation. Provides three endpoint categories:
/_/health: Full aggregated health report/_/health/live: Liveness probe (always 200 if the server is running)/_/health/ready: Readiness probe (200 only when all checks pass)
LifecycleManager
Orchestrates hook execution across seven ordered phases. Hooks are sorted by priority (higher runs first) and can be configured to continue or stop on error. See the Lifecycle guide for full details.
Request Flow
When an HTTP request arrives, it passes through this pipeline:
Extension System
Extensions are self-contained modules that plug into the application lifecycle. Each extension implements the Extension interface:
type Extension interface {
Name() string
Version() string
Description() string
Dependencies() []string
Register(app App) error // Register services with the DI container
Start(ctx context.Context) error // Start the extension
Stop(ctx context.Context) error // Graceful shutdown
Health(ctx context.Context) error // Health check
}Extensions go through a managed lifecycle:
- Dependency resolution: Extensions are topologically sorted based on their declared dependencies.
- Register phase: Each extension calls
Register(app)to add services to the DI container and configure routes. - Start phase: Each extension starts in dependency order. A dependency is fully registered and started before its dependents begin.
- Stop phase: Extensions stop in reverse dependency order during shutdown.
Extensions that implement MiddlewareExtension can provide global middleware that is automatically applied to all routes after registration completes.
Built-In Endpoints
Every Forge application automatically registers these endpoints:
| Endpoint | Description |
|---|---|
/_/info | Application name, version, environment, uptime, Go version, service count, route count, and extension status |
/_/health | Aggregated health check report (when health is enabled) |
/_/health/live | Kubernetes liveness probe |
/_/health/ready | Kubernetes readiness probe |
/_/metrics | Prometheus-format metrics export (when metrics are enabled) |
Startup Sequence
When you call app.Run(), the following happens in order:
Startup steps:
app.Start()is called- PhaseBeforeStart hooks execute
- Extensions are topologically sorted by dependency
- For each extension (in dependency order):
ext.Register(app)is calledext.Start(ctx)is called
- PhaseAfterRegister hooks execute
- Extension middleware is applied globally
- DI container finalization (starts remaining services)
- Health manager is wired to the container
- Observability endpoints are registered
- PhaseAfterStart hooks execute
- HTTP server is created
- Startup banner is printed
- PhaseBeforeRun hooks execute
- HTTP server starts listening
- PhaseAfterRun hooks execute (in background)
- Server blocks, waiting for shutdown signal (SIGINT/SIGTERM)
Shutdown steps:
- HTTP server stops accepting new connections
- In-flight requests drain (up to
ShutdownTimeout) - PhaseBeforeStop hooks execute
- Health manager stops
- Extensions stop in reverse dependency order
- DI container stops all services
- PhaseAfterStop hooks execute
Next Steps
How is this guide?