Integration Patterns Reference
Quick reference for how different parts of Aegis Stack integrate with each other.
Backend Integration Patterns
🔄 Auto-Discovered: Drop files, no registration required
- Middleware:
app/components/backend/middleware/
- Startup Hooks:
app/components/backend/startup/
- Shutdown Hooks:
app/components/backend/shutdown/
📝 Manual Registration: Explicit imports for clarity
- API Routes: Register in
app/components/backend/api/routing.py
- Services: Import explicitly where needed
Service Integration Patterns
Put your business logic in app/services/
and import it explicitly where needed.
Services contain pure business logic functions. Components import and use them.
# app/services/report_service.py
async def generate_monthly_report(user_id: int) -> Report:
# Your business logic here
pass
# app/components/backend/api/reports.py
from app.services.report_service import generate_monthly_report
@router.post("/reports")
async def create_report(user_id: int):
return await generate_monthly_report(user_id)
# app/components/scheduler/main.py
from app.services.report_service import generate_monthly_report
scheduler.add_job(generate_monthly_report, args=[123])
What Goes Where:
Services (app/services/
):
- Database interactions, external API calls, file processing
- Complex business logic and data transformations
- Pure functions that can be unit tested
- Single files (
report_service.py
) or folders (system/health.py
) for complex domains
Components (app/components/
):
- API endpoints, scheduled jobs, UI handlers
- Import services explicitly - no magic auto-discovery
- Keep thin - handle requests, call services, return responses
Why explicit? Makes dependencies clear, prevents surprises, easier testing.
Component Communication
Backend ↔ Services: Direct imports
from app.services.system import get_system_status
CLI ↔ Backend: HTTP API calls
from app.services.system.models import HealthResponse
health_data = HealthResponse.model_validate(api_response.json())
Frontend ↔ Backend: Flet-FastAPI integration
from app.services.system import get_system_status
# Direct function calls within same process
Data Validation Boundaries
Trust Zones: Validate at entry points, trust internally
- API Endpoints: Pydantic
response_model
validates outgoing data - CLI Commands: Pydantic models validate API responses
- Internal Code: Direct model attribute access (no
.get()
patterns)
# Entry point - validate hard
@router.get("/health", response_model=HealthResponse)
async def health_check() -> HealthResponse:
# Internal code - trust the data
status = await get_system_status()
return HealthResponse(healthy=status.overall_healthy, ...)
# CLI - validate API response
health_data = HealthResponse.model_validate(response.json())
# Then trust: health_data.healthy (not health_data.get("healthy"))
Scheduler Integration
Job Registration: Explicit in scheduler component
# app/components/scheduler/main.py
from app.services.reports import generate_daily_report
scheduler.add_job(
generate_daily_report,
trigger="cron",
hour=9, minute=0
)
Service Functions: Pure business logic
# app/services/reports.py
async def generate_daily_report() -> None:
# Pure business logic, no scheduler dependencies
Configuration Access
Global Settings: Available everywhere
from app.core.config import settings
# Use throughout application
database_url = settings.DATABASE_URL
api_timeout = settings.API_TIMEOUT
Constants vs Config:
- Constants (
app.core.constants
): Immutable values (API paths, timeouts) - Config (
app.core.config
): Environment-dependent values (URLs, secrets)
Container Boundaries
Each component manages its own concerns:
- Backend Container: Runs FastAPI + Flet, manages backend hooks
- Scheduler Container: Runs APScheduler, manages scheduled jobs
- Shared: Services, core utilities, configuration
No Cross-Container Hooks: Backend hooks don't affect scheduler, and vice versa.
Key Principles
- Auto-discovery for infrastructure (hooks) → convenience
- Explicit imports for business logic (services, routes) → clarity
- Validate at boundaries → security and reliability
- Trust internally → clean, readable code
- Container isolation → independent scaling