Deploy
Deployment topology management and YAML-based deployment configuration
Overview
The deploy package provides deployment topology management for Lokstra applications. It manages the global registry, handles YAML-based deployment configurations, and provides a 2-layer architecture for deployments (Deployment β Server β App).
Import Path
import (
"github.com/primadi/lokstra/core/deploy"
"github.com/primadi/lokstra/core/deploy/schema"
"github.com/primadi/lokstra/core/deploy/loader"
)
Core Concepts
2-Layer Architecture
Lokstra uses a simplified 2-layer deployment model:
Deployment (Environment: prod, staging, dev)
ββ Servers (Physical/Virtual servers)
ββ Apps (HTTP listeners on ports)
ββ Routers (Route handlers)
Key Points:
- Deployment - Environment grouping (production, staging, development)
- Server - Physical or virtual server instance (BaseURL, services)
- App - HTTP listener on a specific address (port/socket)
- Router - Route handler (manual or auto-generated from services)
Services are at SERVER level - shared across all apps in that server.
Global Registry
Global
Returns the singleton global registry instance.
Signature:
func Global() *GlobalRegistry
Example:
registry := deploy.Global()
// Register service type
registry.RegisterServiceType("user-service", localFactory, remoteFactory,
deploy.WithResource("user", "users"))
// Define service instance
registry.DefineService(&schema.ServiceDef{
Name: "user-svc",
Type: "user-service",
})
GlobalRegistry
Main registry for all global definitions and runtime instances.
Type:
type GlobalRegistry struct {
// Factories
serviceFactories map[string]*ServiceFactoryEntry
middlewareFactories map[string]MiddlewareFactory
// Definitions (YAML or code)
configs map[string]*schema.ConfigDef
middlewares map[string]*schema.MiddlewareDef
services map[string]*schema.ServiceDef
routers map[string]*schema.RouterDef
routerOverrides map[string]*schema.RouterOverrideDef
// Runtime instances
routerInstances sync.Map // map[string]router.Router
serviceInstances sync.Map // map[string]any
middlewareInstances sync.Map // map[string]request.HandlerFunc
// Lazy services
lazyServiceFactories sync.Map // map[string]*LazyServiceEntry
// Topology (2-Layer)
deploymentTopologies sync.Map // map[deploymentName]*DeploymentTopology
serverTopologies sync.Map // map[compositeKey]*ServerTopology
}
Registration Options
Service Registration Options
WithResource
Specifies resource names for auto-router generation.
Signature:
func WithResource(singular, plural string) RegisterServiceTypeOption
Example:
deploy.WithResource("user", "users")
deploy.WithResource("person", "people")
WithConvention
Specifies routing convention (default: βrestβ).
Signature:
func WithConvention(convention string) RegisterServiceTypeOption
Example:
deploy.WithConvention("rest")
deploy.WithConvention("rpc")
WithDependencies
Declares service dependencies for automatic injection.
Signature:
func WithDependencies(deps ...string) RegisterServiceTypeOption
Example:
deploy.WithDependencies("db", "cache", "logger")
WithPathPrefix
Sets path prefix for all routes.
Signature:
func WithPathPrefix(prefix string) RegisterServiceTypeOption
Example:
deploy.WithPathPrefix("/api/v1")
deploy.WithPathPrefix("/api/v2")
WithMiddleware
Attaches middleware to all service routes.
Signature:
func WithMiddleware(names ...string) RegisterServiceTypeOption
Example:
deploy.WithMiddleware("auth", "logger", "rate-limiter")
WithRouteOverride
Customizes path for specific methods.
Signature:
func WithRouteOverride(methodName, pathSpec string) RegisterServiceTypeOption
Example:
deploy.WithRouteOverride("Login", "POST /auth/login")
deploy.WithRouteOverride("Logout", "POST /auth/logout")
WithHiddenMethods
Excludes methods from auto-router generation.
Signature:
func WithHiddenMethods(methods ...string) RegisterServiceTypeOption
Example:
deploy.WithHiddenMethods("InternalHelper", "validateUser")
Middleware Registration Options
WithAllowOverride
Allows overriding existing middleware types.
Signature:
func WithAllowOverride(allow bool) MiddlewareTypeOption
Example:
deploy.Global().RegisterMiddlewareType("logger", loggerFactory,
deploy.WithAllowOverride(true))
WithAllowOverrideForName
Allows overriding existing middleware names.
Signature:
func WithAllowOverrideForName(allow bool) MiddlewareNameOption
Lazy Service Registration Options
WithRegistrationMode
Sets registration mode for lazy services.
Signature:
func WithRegistrationMode(mode LazyServiceMode) LazyServiceOption
Modes:
const (
LazyServicePanic LazyServiceMode = iota // Panic if exists (default)
LazyServiceSkip // Skip if exists (idempotent)
LazyServiceOverride // Override existing
)
Example:
deploy.WithRegistrationMode(deploy.LazyServiceSkip)
deploy.WithRegistrationMode(deploy.LazyServiceOverride)
Loader Functions
LoadConfig
Loads deployment configuration from YAML file(s).
Signature:
func LoadConfig(paths ...string) (*schema.DeployConfig, error)
Parameters:
paths- One or more YAML file paths
Returns:
*schema.DeployConfig- Merged configurationerror- Error if loading or validation fails
Example:
// Single file
config, err := loader.LoadConfig("config/deployment.yaml")
if err != nil {
log.Fatal(err)
}
// Multiple files (merged)
config, err := loader.LoadConfig(
"config/base.yaml",
"config/services.yaml",
"config/production.yaml",
)
if err != nil {
log.Fatal(err)
}
Features:
- β Multi-file merging
- β JSON schema validation
- β Unknown field detection
- β Dependency validation
ValidateConfig
Validates deployment configuration against JSON schema.
Signature:
func ValidateConfig(config *schema.DeployConfig) error
Example:
config, _ := loader.LoadConfig("config/app.yaml")
if err := loader.ValidateConfig(config); err != nil {
log.Fatal(err)
}
Topology Management
DeploymentTopology
Deployment-level configuration.
Type:
type DeploymentTopology struct {
Name string
ConfigOverrides map[string]any
Servers map[string]*ServerTopology
}
Example:
deployments:
production:
config-overrides:
log.level: INFO
db.pool_size: 100
servers:
api-server:
# ...
ServerTopology
Server-level topology (services shared across apps).
Type:
type ServerTopology struct {
Name string
DeploymentName string
BaseURL string
Services []string // Server-level services (shared)
RemoteServices map[string]string // serviceName -> remoteBaseURL
Apps []*AppTopology
}
Example:
servers:
api-server:
base-url: http://api.example.com
apps:
- addr: ":8080"
routers:
- user-router
AppTopology
App-level topology (HTTP listener).
Type:
type AppTopology struct {
Addr string
Routers []string
}
Complete Examples
Basic Deployment
# config/deployment.yaml
configs:
app.name: "MyApp"
app.version: "1.0.0"
service-definitions:
user-service:
type: user-service-factory
depends-on:
- db-service
db-service:
type: postgres-factory
config:
dsn: "postgresql://localhost/myapp"
router-definitions:
custom:
deployments:
production:
servers:
api-server:
base-url: http://api.example.com
apps:
- addr: ":8080"
routers:
- user-service-router
Load and Use:
package main
import (
"github.com/primadi/lokstra/core/deploy"
"github.com/primadi/lokstra/core/deploy/loader"
)
func main() {
// Load config
config, err := loader.LoadConfig("config/deployment.yaml")
if err != nil {
log.Fatal(err)
}
// Register definitions to global registry
for name, def := range config.ServiceDefinitions {
deploy.Global().DefineService(def)
}
for name, def := range config.RouterDefinitions {
deploy.Global().DefineRouter(name, def)
}
// Build and run deployment
// ... (framework handles this automatically)
}
Multi-File Deployment
# config/01-base.yaml
configs:
app.name: "MyApp"
service-definitions:
db-service:
type: postgres-factory
# config/02-services.yaml
service-definitions:
user-service:
type: user-service-factory
depends-on:
- db-service
order-service:
type: order-service-factory
depends-on:
- db-service
- user-service
# config/03-production.yaml
configs:
db.dsn: "${DATABASE_URL}"
log.level: "INFO"
deployments:
production:
servers:
api-server:
base-url: https://api.example.com
apps:
- addr: ":443"
routers:
- user-router
- order-router
Load:
config, err := loader.LoadConfig(
"config/01-base.yaml",
"config/02-services.yaml",
"config/03-production.yaml",
)
Multi-Environment Deployment
# config/base.yaml
service-definitions:
user-service:
type: user-service-factory
# config/development.yaml
deployments:
development:
config-overrides:
log.level: DEBUG
servers:
dev-server:
base-url: http://localhost:8080
apps:
- addr: ":8080"
routers:
- user-service-router
# config/production.yaml
deployments:
production:
config-overrides:
log.level: INFO
servers:
api-server-1:
base-url: https://api-1.example.com
apps:
- addr: ":443"
routers:
- user-service-router
api-server-2:
base-url: https://api-2.example.com
apps:
- addr: ":443"
routers:
- user-service-router
External Service Integration
service-definitions:
user-service:
type: user-service-factory
deployments:
production:
servers:
api-server:
base-url: https://api.example.com
apps:
- addr: ":443"
routers:
- user-service-router
- payment-service-router # Auto-generated from external service
Router Inline Overrides
service-definitions:
user-service:
type: user-service-factory
router-definitions:
user-service-router: # Service derived from name
path-prefix: /api/v1
middlewares:
- auth
- logger
custom:
- name: Login
method: POST
path: /auth/login
- name: Logout
method: POST
path: /auth/logout
deployments:
production:
servers:
api-server:
base-url: https://api.example.com
apps:
- addr: ":443"
routers:
- user-service-router
Published Services (Shorthand)
service-definitions:
user-service:
type: user-service-factory
order-service:
type: order-service-factory
deployments:
production:
servers:
api-server:
base-url: https://api.example.com
# Shorthand: automatically creates routers
addr: ":443"
published-services:
- user-service
- order-service
Equivalent to:
router-definitions:
user-service-router:
# Auto-generated from service metadata
order-service-router:
# Auto-generated from service metadata
deployments:
production:
servers:
api-server:
base-url: https://api.example.com
apps:
- addr: ":443"
routers:
- user-service-router
- order-service-router
Handler Configurations
# Full example with reverse proxies, SPAs, and static files
service-definitions:
api-service:
type: api-service-factory
router-definitions:
api-service-router:
convention: rest
deployments:
production:
servers:
app-server:
base-url: https://example.com
apps:
# Backend API server
- addr: ":8080"
routers:
- api-service-router
# Frontend gateway server
- addr: ":3000"
# Proxy API requests to backend
reverse-proxies:
- prefix: /api
target: http://localhost:8080
strip-prefix: false
# Proxy to legacy system with rewrite
- prefix: /legacy
target: http://legacy-system:9000
strip-prefix: true
rewrite:
path-pattern: "^/legacy/(.*)$"
path-replacement: "/v1/$1"
# Serve SPA applications
mount-spa:
- prefix: /admin
dir: ./dist/admin
- prefix: /
dir: ./dist/app
# Serve static assets
mount-static:
- prefix: /assets
dir: ./public/assets
- prefix: /uploads
dir: ./storage/uploads
Code Loading:
package main
import (
"log"
"github.com/primadi/lokstra/core/deploy/loader"
"github.com/primadi/lokstra/lokstra_registry"
)
func main() {
// Load configuration
config, err := loader.LoadConfig("config.yaml")
if err != nil {
log.Fatal(err)
}
// Handler configurations are automatically applied
// during server initialization via applyAppHandlerConfigurations()
// Run deployment
lokstra_registry.RunServerFromConfig()
}
Handler Application Order:
- Reverse Proxies - Applied first using
app.AddReverseProxies() - SPA Mounts - Applied using
lokstra_handler.MountSpa() - Static Mounts - Applied using
lokstra_handler.MountStatic()
Best Practices
1. Use Multi-File Configuration
# β
Good: Separate concerns
config/
βββ 01-base.yaml # Base config
βββ 02-services.yaml # Service definitions
βββ 03-routers.yaml # Router definitions
βββ 04-production.yaml # Environment-specific
# π« Avoid: Everything in one file
config/
βββ monolith.yaml # 500+ lines
2. Use Config Overrides per Environment
# β
Good: Environment-specific overrides
deployments:
production:
config-overrides:
log.level: INFO
db.pool_size: 100
development:
config-overrides:
log.level: DEBUG
db.pool_size: 10
# π« Avoid: Hardcoded values
service-definitions:
db-service:
config:
pool_size: 10 # Same for all environments
3. Validate Configuration Early
// β
Good: Validate on load
config, err := loader.LoadConfig("config/app.yaml")
if err != nil {
log.Fatalf("Config validation failed: %v", err)
}
// π« Avoid: No validation
config := loadYAML("config/app.yaml") // May have errors
4. Use External Services for Third-Party APIs
# β
Good: External service definitions
external-service-definitions:
stripe-api:
url: https://api.stripe.com
type: stripe-client-factory
# π« Avoid: Mixing with local services
service-definitions:
stripe-api: # This is external, not local!
type: stripe-client-factory
5. Document Dependencies
# β
Good: Clear dependencies
service-definitions:
order-service:
type: order-service-factory
depends-on:
- user-service
- payment-service
- inventory-service
# π« Avoid: Hidden dependencies
service-definitions:
order-service:
type: order-service-factory
# Dependencies not declared
See Also
- Config - Configuration management
- Schema - YAML schema definitions
- lokstra_registry - Registry API
- Service Registration - Service patterns
Related Guides
- Deployment Essentials - Deployment basics
- Multi-Environment Setup - Environment strategies
- Microservices Architecture - Distributed deployment