On this page
The Application Layer coordinates domain services without containing business logic itself. It handles lifecycle management, scheduling, and cross-domain workflows.
Characteristics
- Coordinates services - Orchestrates domain crates
- Manages lifecycle - Startup, shutdown, health checks
- No business logic - Delegates to domain crates
- No direct database access - Uses domain services
Crates
| Crate | Purpose |
|---|---|
| systemprompt-runtime | AppContext, lifecycle, module registry |
| systemprompt-scheduler | Cron scheduling, job execution |
| systemprompt-generator | Static site generation |
| systemprompt-sync | Cloud synchronization |
Dependency Rules
Application layer crates:
- Can depend on Shared layer crates
- Can depend on Infrastructure layer crates
- Can depend on Domain layer crates
- Cannot depend on Entry layer crates
Common Patterns
AppContext
The runtime crate provides AppContext, the central coordination point:
pub struct AppContext {
pub config: Config,
pub database: Arc<DatabasePool>,
pub events: Arc<EventBus>,
pub extensions: ExtensionRegistry,
// Domain services
pub users: Arc<UserService>,
pub content: Arc<ContentService>,
pub agents: Arc<AgentService>,
// ...
}
impl AppContext {
pub async fn new(config: Config) -> Result<Self> {
// Initialize infrastructure
let database = DatabasePool::connect(&config.database).await?;
let events = EventBus::new();
// Discover extensions
let extensions = ExtensionRegistry::discover();
// Run migrations
for ext in extensions.schema_extensions() {
ext.migrate(&database).await?;
}
// Initialize domain services
let users = UserService::new(database.clone(), events.clone());
let content = ContentService::new(database.clone(), events.clone());
Ok(Self { config, database, events, extensions, users, content, ... })
}
}
Job Scheduling
use systemprompt_scheduler::{Scheduler, JobDefinition};
// Define jobs in config
let scheduler = Scheduler::new(&ctx);
// Register jobs from extensions
for ext in ctx.extensions.job_extensions() {
for job in ext.jobs() {
scheduler.register(job);
}
}
// Start scheduler
scheduler.start().await;
Static Generation
use systemprompt_generator::Generator;
let generator = Generator::new(&ctx);
// Generate all content
generator.build_all().await?;
// Or generate specific content
generator.build_content(content_id).await?;
Cloud Sync
use systemprompt_sync::SyncService;
let sync = SyncService::new(&ctx);
// Sync from cloud to local
sync.pull(SyncOptions {
source: "content",
direction: SyncDirection::FromCloud,
}).await?;
// Sync from local to cloud
sync.push(SyncOptions {
source: "agents",
direction: SyncDirection::ToCloud,
}).await?;
Layer Diagram
┌────────────────────────────────────────────────────────────┐
│ APPLICATION LAYER │
│ │
│ ┌─────────┐ ┌───────────┐ ┌───────────┐ ┌──────┐ │
│ │ runtime │ │ scheduler │ │ generator │ │ sync │ │
│ └─────────┘ └───────────┘ └───────────┘ └──────┘ │
│ │
│ ↓ │
│ Depends on DOMAIN, INFRA, SHARED │
└────────────────────────────────────────────────────────────┘