The Infrastructure Layer provides stateless utilities that domain crates build upon. These crates handle cross-cutting concerns like database access, logging, configuration, and security.

Characteristics

  • May have external I/O - Database, network, filesystem
  • No business logic - Technical concerns only
  • Reusable - Used across all domain crates
  • Configuration-driven - Behavior controlled by config

Crates

Crate Purpose
systemprompt-database PostgreSQL/SQLx, connection pooling, migrations
systemprompt-logging Tracing setup, structured logging
systemprompt-config Profile-based configuration
systemprompt-events Event bus, SSE, broadcasting
systemprompt-security JWT validation, authentication
systemprompt-cloud Cloud API client, tenant management
systemprompt-loader File discovery, module loading

Dependency Rules

Infrastructure layer crates:

  • Can depend on Shared layer crates
  • Can depend on each other within the layer
  • Cannot depend on Domain, Application, or Entry crates

Common Patterns

Database Access

use systemprompt_database::DatabasePool;

// Get pool from AppContext
let pool = ctx.database.clone();

// Execute queries with SQLx
let users = sqlx::query_as!(User,
    "SELECT * FROM users WHERE tenant_id = $1",
    tenant_id
)
.fetch_all(&*pool)
.await?;

Event Publishing

use systemprompt_events::{EventBus, Event};

// Define an event
#[derive(Event)]
struct UserCreated {
    user_id: UserId,
    email: String,
}

// Publish
events.emit(UserCreated { user_id, email }).await;

// Subscribe
events.subscribe::<UserCreated, _>(|event| async {
    println!("User created: {}", event.user_id);
});

Security Middleware

use systemprompt_security::{JwtValidator, Claims};

// Validate JWT from request
let claims: Claims = validator.validate(&token)?;

// Check scopes
if !claims.has_scope("admin") {
    return Err(Forbidden);
}

Configuration Loading

use systemprompt_config::Config;

// Load active profile
let config = Config::load()?;

// Access values
let db_host = config.database.host;
let api_port = config.api.port;

Layer Diagram

┌────────────────────────────────────────────────────────────┐
│                   INFRASTRUCTURE LAYER                      │
│                                                            │
│  ┌──────────┐  ┌─────────┐  ┌────────┐  ┌────────┐       │
│  │ database │  │ logging │  │ config │  │ events │       │
│  └──────────┘  └─────────┘  └────────┘  └────────┘       │
│                                                            │
│  ┌──────────┐  ┌───────┐  ┌────────┐                      │
│  │ security │  │ cloud │  │ loader │                      │
│  └──────────┘  └───────┘  └────────┘                      │
│                                                            │
│                         ↓                                  │
│              Depends on SHARED LAYER                       │
└────────────────────────────────────────────────────────────┘