Service Generation Guide
Complete guide for using Saga's service generation system to quickly bootstrap services with automatic registration.
Table of Contents
- Overview
- Quick Start
- Supported Languages
- Basic Usage
- Template Customization
- Language-Specific Guides
- Integration Examples
- Troubleshooting
Overview
Saga's service generation system (saga generate service) automatically creates service registration code for different programming languages. This eliminates boilerplate and ensures consistent service registration patterns across your microservices.
What Gets Generated
For each language, the generator creates:
- Configuration management - Environment-based config with sensible defaults
- Service registration - Automatic registration with Saga on startup
- Heartbeat management - Automatic TTL refresh to keep registrations alive
- Graceful shutdown - Automatic unregistration on service shutdown
- Project structure - Language-specific project files and dependencies
Quick Start
Generate a service in any supported language:
# Generate Rust service
saga generate service payment-service --language rust --port 8001
# Generate Python service
saga generate service auth-service --language python --port 8002
# Generate Elixir service
saga generate service notification --language elixir --port 8003
# Generate Node.js service
saga generate service gateway --language node --port 8000
This creates a complete service skeleton with Saga integration ready to use.
Supported Languages
- Rust (
rust) - Full async/await with tokio - Python (
python) - Async with asyncio, FastAPI-ready - Elixir (
elixir) - GenServer-based with Phoenix-ready - Node.js (
node) - Async/await with Express-ready
Basic Usage
Command Syntax
saga generate service <NAME> [options]
Required Arguments
NAME- Service name (unique identifier)
Options
--language <LANG>- Target language:rust,python,elixir,node(required)--path <PATH>- Output path (optional, default: current directory)--capabilities <CAPS>- Service capabilities as comma-separated list--port <PORT>- Service port (optional, default:8000)--template <PATH>- Custom template directory (advanced)--redis-url <URL>- Redis URL for generated code--saga-url <URL>- Saga service URL (default:http://localhost:8030)
Examples
# Basic service generation
saga generate service payment-service --language rust
# With custom port
saga generate service auth-service --language python --port 8002
# With capabilities
saga generate service gateway --language node \
--capabilities graphql,rest --port 8000
# Custom output path
saga generate service notification --language elixir \
--path ./services/notification
# Custom Redis and Saga URLs
saga generate service payment-service --language rust \
--redis-url redis://redis.example.com:6379 \
--saga-url http://saga.example.com:8030
Template Customization
Using Custom Templates
You can use custom templates by specifying the --template option:
saga generate service my-service --language rust \
--template ./custom-templates
The template directory should follow this structure:
custom-templates/
├── rust/
│ ├── Cargo.toml.template
│ ├── config.rs.template
│ ├── main.rs.template
│ └── registration.rs.template
├── python/
│ ├── config.py.template
│ ├── main.py.template
│ ├── registration.py.template
│ └── requirements.txt.template
└── ...
Template Variables
All templates support these variables:
{{service_name}}- Service name (e.g.,payment-service){{service_url}}- Full service URL (e.g.,http://localhost:8001){{service_port}}- Service port number (e.g.,8001){{capabilities}}- Formatted capabilities list (language-specific){{redis_url}}- Redis connection URL{{saga_url}}- Saga service URL
Language-Specific Variables:
- Elixir only:
{{service_module_name}}- PascalCase module name (e.g.,PaymentService){{ServiceName}}- Alias for module name{{service_atom}}- Snake case atom (e.g.,payment_service)
Template Format
Templates use {{variable_name}} syntax for variable substitution. Example:
// In main.rs.template
info!("Starting {{service_name}} service");
let url = "{{service_url}}";
let port = {{service_port}};
Language-Specific Guides
Rust
Generated Files:
Cargo.toml- Project dependencies and metadatasrc/config.rs- Configuration managementsrc/main.rs- Main entry point with registrationsrc/registration.rs- Service registration logic
Features:
- Async/await with tokio
- Automatic registration on startup
- Background heartbeat task
- Graceful shutdown with unregistration
- Error handling with anyhow
Example Usage:
# Generate Rust service
saga generate service payment-service --language rust --port 8001
# Navigate to generated service
cd payment-service
# Build and run
cargo build --release
cargo run
# Or with custom Redis URL
REDIS_URL=redis://localhost:6379 cargo run
Integration:
The generated Rust service includes:
- Configuration from environment variables
- Automatic service registration
- Heartbeat every 30 seconds
- Graceful shutdown handling
Add your service logic in main.rs after registration.
Python
Generated Files:
config.py- Configuration managementmain.py- Main entry point with FastAPI-ready structureregistration.py- Service registration clientrequirements.txt- Python dependencies
Features:
- Async/await with asyncio
- FastAPI-ready lifespan management
- Automatic registration/unregistration
- Background heartbeat task
- Environment-based configuration
Example Usage:
# Generate Python service
saga generate service auth-service --language python --port 8002
# Navigate to generated service
cd auth-service
# Install dependencies
pip install -r requirements.txt
# Run service
python main.py
# Or with environment variables
REDIS_URL=redis://localhost:6379 python main.py
Integration with FastAPI:
The generated code includes a lifespan context manager ready for FastAPI:
from fastapi import FastAPI
from main import lifespan
app = FastAPI(lifespan=lifespan)
@app.get("/")
async def root():
return {"message": "Hello from auth-service"}
Elixir
Generated Files:
mix.exs- Mix project configurationconfig/config.exs- Application configurationlib/{ServiceName}/application.ex- Application supervision treelib/{ServiceName}/service_registration.ex- Registration GenServer
Features:
- GenServer-based registration
- Supervision tree integration
- Automatic registration on application start
- Background heartbeat process
- Graceful shutdown handling
- Phoenix-ready structure
Example Usage:
# Generate Elixir service
saga generate service notification --language elixir --port 8003
# Navigate to generated service
cd Notification # Module name in PascalCase
# Install dependencies
mix deps.get
# Run service
mix run --no-halt
# Or in interactive mode
iex -S mix
Integration with Phoenix:
The generated structure follows Phoenix conventions. To add Phoenix:
# In mix.exs, add:
{:phoenix, "~> 1.7"}
# Then generate Phoenix app:
mix phx.new . --no-ecto
The service registration GenServer will start automatically with your application.
Node.js
Generated Files:
package.json- Node.js project and dependenciesconfig.js- Configuration managementindex.js- Main entry pointregistration.js- Service registration client
Features:
- Async/await with native Promise support
- Express-ready structure
- Automatic registration on startup
- Background heartbeat interval
- Graceful shutdown with unregistration
- Environment-based configuration
Example Usage:
# Generate Node.js service
saga generate service gateway --language node --port 8000
# Navigate to generated service
cd gateway
# Install dependencies
npm install
# Run service
node index.js
# Or with npm scripts
npm start
# With environment variables
REDIS_URL=redis://localhost:6379 node index.js
Integration with Express:
The generated code is ready for Express integration:
const express = require('express');
const { main } = require('./index');
const app = express();
// Your routes here
app.get('/', (req, res) => {
res.json({ message: 'Hello from gateway' });
});
// Start service (includes Saga registration)
main().then(() => {
app.listen(config.port, () => {
console.log(`Server running on port ${config.port}`);
});
});
Integration Examples
Complete Workflow
-
Generate Service:
saga generate service payment-service --language rust \
--port 8001 --capabilities rest -
Add Your Logic:
// In src/main.rs, add your service logic:
use actix_web::{web, App, HttpServer};
#[actix_web::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// ... existing registration code ...
// Add your HTTP server
HttpServer::new(|| {
App::new()
.route("/health", web::get().to(health))
.route("/payments", web::post().to(create_payment))
})
.bind(format!("{}:{}", config.host, config.port))?
.run()
.await?;
Ok(())
} -
Register with Saga: The service automatically registers on startup. Verify:
saga service list -
Test Service:
# Check service health
saga service health payment-service
# Get service details
saga service get payment-service
Multi-Language Service
Generate services in different languages that all register with the same Saga instance:
# Gateway (Node.js)
saga generate service gateway --language node --port 8000 \
--capabilities graphql,rest
# Payment Service (Rust)
saga generate service payment --language rust --port 8001 \
--capabilities rest
# Auth Service (Python)
saga generate service auth --language python --port 8002 \
--capabilities rest
# Notification Service (Elixir)
saga generate service notification --language elixir --port 8003 \
--capabilities rest
All services will automatically discover each other via Saga.
Custom Configuration
Override default configuration in generated code:
Rust:
// In src/config.rs
pub struct Config {
pub redis_url: String,
pub saga_url: String,
pub host: String,
pub port: u16,
}
impl Config {
pub fn load() -> Result<Self> {
Ok(Config {
redis_url: std::env::var("REDIS_URL")
.unwrap_or_else(|_| "redis://localhost:6379".to_string()),
saga_url: std::env::var("SAGA_URL")
.unwrap_or_else(|_| "http://localhost:8030".to_string()),
// ... other config ...
})
}
}
Python:
# In config.py
import os
class Config:
def __init__(self):
self.redis_url = os.getenv("REDIS_URL", "redis://localhost:6379")
self.saga_url = os.getenv("SAGA_URL", "http://localhost:8030")
# ... other config ...
Troubleshooting
Template Not Found
Error: Template not found: /path/to/template
Solution:
- Ensure you're running saga from the correct directory
- Check that templates exist in
shared/saga/templates/ - Use
--templateto specify custom template directory
Invalid Language
Error: Invalid language: xyz
Solution:
- Use one of:
rust,python,elixir,node - Check spelling and case sensitivity
Service Already Exists
Error: Directory already exists: payment-service
Solution:
- Use
--pathto specify a different output directory - Remove existing directory first
- Use a different service name
Registration Fails
Error: Failed to register service: Connection refused
Solution:
- Ensure Saga service is running:
saga start - Check Redis connection:
saga debug redis "PING" - Verify
SAGA_URLenvironment variable or config
Heartbeat Not Working
Issue: Service registration expires
Solution:
- Check that heartbeat task/process is running
- Verify Redis connection is stable
- Check service logs for heartbeat errors
- Manually trigger heartbeat:
saga service heartbeat <name>
Custom Template Variables Not Working
Issue: Variables not being substituted
Solution:
- Use
{{variable_name}}syntax (double curly braces) - Check variable names match exactly
- Verify template file encoding (UTF-8)
Best Practices
-
Service Naming:
- Use kebab-case:
payment-service,auth-service - Make names descriptive and unique
- Avoid special characters
- Use kebab-case:
-
Port Management:
- Use consistent port ranges per environment
- Document port assignments
- Avoid hardcoded ports in code
-
Capabilities:
- Be specific about capabilities
- Update capabilities when service changes
- Use comma-separated list format
-
Configuration:
- Use environment variables for sensitive data
- Provide sensible defaults
- Validate configuration on startup
-
Error Handling:
- Handle registration failures gracefully
- Log all registration events
- Retry failed registrations
-
Testing:
- Test service registration in CI/CD
- Verify heartbeat functionality
- Test graceful shutdown
Advanced Usage
Custom Template Directory
Create your own templates:
# Create template structure
mkdir -p my-templates/rust
cp shared/saga/templates/rust/* my-templates/rust/
# Modify templates as needed
vim my-templates/rust/main.rs.template
# Generate with custom templates
saga generate service my-service --language rust \
--template ./my-templates
Batch Generation
Generate multiple services:
#!/bin/bash
# Generate all services for a microservices architecture
saga generate service api-gateway --language node --port 8000 --capabilities graphql,rest
saga generate service auth-service --language python --port 8001 --capabilities rest
saga generate service payment-service --language rust --port 8002 --capabilities rest
saga generate service notification-service --language elixir --port 8003 --capabilities rest
CI/CD Integration
Generate services in CI/CD pipelines:
# .github/workflows/generate-services.yml
- name: Generate Service
run: |
saga generate service ${{ env.SERVICE_NAME }} \
--language ${{ env.SERVICE_LANGUAGE }} \
--port ${{ env.SERVICE_PORT }} \
--capabilities ${{ env.SERVICE_CAPABILITIES }}
Related Documentation
- CLI Reference - Complete CLI command reference
- Getting Started - Saga service overview
- API Documentation - HTTP API reference