-
Notifications
You must be signed in to change notification settings - Fork 0
Guide Application Lifecycle
ARO applications have a well-defined lifecycle from startup to shutdown. This chapter explains how to manage your application's lifecycle.
┌─────────────────────────────────────────────────────┐
│ Application Lifecycle │
├─────────────────────────────────────────────────────┤
│ │
│ 1. Load all .aro files │
│ 2. Compile and validate │
│ 3. Register feature sets with event bus │
│ 4. Execute Application-Start │
│ 5. Enter event loop │
│ │ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ Handle Events │◄──── HTTP, Files, Sockets, │
│ │ (event loop) │ Domain Events │
│ └────────┬────────┘ │
│ │ │
│ ▼ (shutdown signal) │
│ │
│ 6. Stop accepting new events │
│ 7. Wait for pending events │
│ 8. Execute Application-End │
│ 9. Stop services │
│ 10. Exit │
│ │
└─────────────────────────────────────────────────────┘
The entry point for every ARO application.
- Exactly one per application
- Must be named
Application-Start - Must return a status
(Application-Start: My Application) {
<Log> "Starting application..." to the <console>.
<Return> an <OK: status> for the <startup>.
}
(Application-Start: E-Commerce Platform) {
<Log> "Starting E-Commerce Platform..." to the <console>.
(* Load configuration *)
<Read> the <config: JSON> from the <file: "./config.json">.
<Publish> as <app-config> <config>.
(* Initialize database connection *)
<Connect> to <host: "${config.database.host}"> on port <config: database.port> as <database>.
(* Start HTTP server *)
<Start> the <http-server> on port <config: server.port>.
(* Start file watcher for uploads *)
<Watch> the <directory: "./uploads"> as <upload-watcher>.
(* Start background scheduler *)
<Start> the <scheduler>.
<Log> "Platform ready on port ${config.server.port}" to the <console>.
<Return> an <OK: status> for the <startup>.
}
Statements execute sequentially, so order matters:
(Application-Start: Ordered Initialization) {
(* 1. Load config first - other steps depend on it *)
<Read> the <config> from the <file: "./config.json">.
<Publish> as <app-config> <config>.
(* 2. Initialize database - services need it *)
<Connect> to <database-host> as <database>.
(* 3. Start services - they use config and database *)
<Start> the <http-server> on port <config: port>.
<Return> an <OK: status> for the <startup>.
}
Exit handlers for cleanup when the application stops.
Called on graceful shutdown (SIGTERM, SIGINT, or programmatic stop):
(Application-End: Success) {
<Log> "Shutting down gracefully..." to the <console>.
(* Stop accepting new requests *)
<Stop> the <http-server>.
(* Close database connections *)
<Close> the <database-connections>.
<Log> "Shutdown complete. Goodbye!" to the <console>.
<Return> an <OK: status> for the <shutdown>.
}
Called when the application crashes or encounters a fatal error:
(Application-End: Error) {
<Extract> the <error> from the <shutdown: error>.
<Extract> the <code> from the <shutdown: code>.
<Extract> the <reason> from the <shutdown: reason>.
<Log> "FATAL ERROR: ${reason}" to the <console>.
<Log> <error> to the <console>.
(* Send alert to operations *)
<Send> the <crash-alert> to the <ops-webhook> with {
service: "E-Commerce Platform",
error: <error>,
code: <code>,
reason: <reason>,
timestamp: <current-time>
}.
(* Attempt graceful cleanup *)
<Close> the <database-connections>.
<Return> an <OK: status> for the <error-handling>.
}
Available variables in Application-End handlers:
| Variable | Description | Available In |
|---|---|---|
<shutdown: reason> |
Human-readable reason | Both |
<shutdown: code> |
Exit code (0 = success) | Both |
<shutdown: signal> |
Signal name (SIGTERM, etc.) | Success |
<shutdown: error> |
Error object | Error only |
- Both handlers are optional
- At most one of each per application
- Error handler only runs on errors
- Success handler only runs on graceful shutdown
aro run ./MyAppThe application runs and exits when Application-Start completes.
For servers that should run indefinitely, use the <Keepalive> action:
(Application-Start: My Server) {
<Start> the <http-server> on port 8080.
<Keepalive> the <application> for the <events>.
<Return> an <OK: status> for the <startup>.
}
The <Keepalive> action blocks until interrupted (Ctrl+C or kill signal).
When you send SIGTERM or SIGINT:
- The event loop stops accepting new events
- Pending events complete (with timeout)
-
Application-End: Successexecutes - Services stop
- Application exits with code 0
When an unhandled error occurs:
- The error is caught
-
Application-End: Errorexecutes - Services stop (best effort)
- Application exits with non-zero code
(Application-Start: Web Server) {
<Start> the <http-server> on port 8080.
<Keepalive> the <application> for the <events>.
<Return> an <OK: status> for the <startup>.
}
(Application-End: Success) {
<Stop> the <http-server>.
<Return> an <OK: status> for the <shutdown>.
}
(Application-Start: File Processor) {
<Watch> the <directory: "./inbox"> as <file-watcher>.
<Keepalive> the <application> for the <events>.
<Return> an <OK: status> for the <startup>.
}
(Application-End: Success) {
<Stop> the <file-watcher>.
<Return> an <OK: status> for the <shutdown>.
}
(Application-Start: Socket Server) {
<Listen> on port 9000 as <socket-server>.
<Keepalive> the <application> for the <events>.
<Return> an <OK: status> for the <startup>.
}
(Application-End: Success) {
<Close> the <socket-server>.
<Return> an <OK: status> for the <shutdown>.
}
(Application-Start: Full Stack) {
(* HTTP API *)
<Start> the <http-server> on port 8080.
(* WebSocket server *)
<Listen> on port 8081 as <websocket-server>.
(* File watcher *)
<Watch> the <directory: "./uploads"> as <upload-watcher>.
(* Background jobs *)
<Start> the <job-scheduler>.
(* Keep the application running *)
<Keepalive> the <application> for the <events>.
<Return> an <OK: status> for the <startup>.
}
(Application-End: Success) {
(* Stop in reverse order *)
<Stop> the <job-scheduler>.
<Stop> the <upload-watcher>.
<Close> the <websocket-server>.
<Stop> the <http-server>.
<Return> an <OK: status> for the <shutdown>.
}
(Application-Start: Configured App) {
<Read> the <config: JSON> from the <file: "./config.json">.
<Publish> as <app-config> <config>.
<Start> the <http-server> on port <config: port>.
<Return> an <OK: status> for the <startup>.
}
(Application-Start: Environment Config) {
<Extract> the <port> from the <environment: PORT>.
<Extract> the <db-url> from the <environment: DATABASE_URL>.
<Connect> to <db-url> as <database>.
<Start> the <http-server> on port <port>.
<Return> an <OK: status> for the <startup>.
}
(Application-Start: Config with Defaults) {
<Extract> the <port> from the <environment: PORT>.
<Set> the <port> to 8080 when <port> is empty.
<Start> the <http-server> on port <port>.
<Return> an <OK: status> for the <startup>.
}
Set up health check endpoints using contract-first HTTP. Define routes in openapi.yaml:
openapi.yaml:
paths:
/health:
get:
operationId: healthCheck
/ready:
get:
operationId: readinessCheckmain.aro:
(Application-Start: Healthy App) {
<Set> the <startup-time> to <current-time>.
<Publish> as <app-startup-time> <startup-time>.
<Keepalive> the <application> for the <events>.
<Return> an <OK: status> for the <startup>.
}
(healthCheck: Health API) {
<Create> the <health> with {
status: "healthy",
uptime: <current-time> - <app-startup-time>,
version: "1.0.0"
}.
<Return> an <OK: status> with <health>.
}
(readinessCheck: Health API) {
(* Check dependencies *)
<Check> the <database-connection>.
<Check> the <cache-connection>.
<Return> a <ServiceUnavailable: status> with { ready: false } when <checks: allPassed> is false.
<Return> an <OK: status> with { ready: true }.
}
(Application-Start: Fail Fast) {
(* Check critical config first *)
<Read> the <config> from the <file: "./config.json">.
when <config: database> is empty {
<Log> "Missing database configuration" to the <console>.
<Throw> a <ConfigurationError> for the <missing: database>.
}
(* Then initialize services *)
<Connect> to <config: database.url> as <database>.
<Start> the <http-server> on port <config: port>.
<Return> an <OK: status> for the <startup>.
}
(Application-End: Success) {
<Log> "Initiating graceful shutdown..." to the <console>.
(* 1. Stop accepting new work *)
<Stop> the <http-server>.
(* 2. Wait for in-progress work *)
<Wait> for the <pending-requests> with timeout 30.
(* 3. Close external connections *)
<Close> the <database-connections>.
<Close> the <cache-connections>.
<Log> "Shutdown complete" to the <console>.
<Return> an <OK: status> for the <shutdown>.
}
(Application-Start: Observable App) {
<Log> "APPLICATION_STARTING" to the <console>.
<Start> the <http-server> on port 8080.
<Log> "APPLICATION_READY" to the <console>.
<Return> an <OK: status> for the <startup>.
}
(Application-End: Success) {
<Log> "APPLICATION_STOPPING" to the <console>.
<Stop> the <http-server>.
<Log> "APPLICATION_STOPPED" to the <console>.
<Return> an <OK: status> for the <shutdown>.
}
- HTTP Services - HTTP server and client
- File System - File operations and watching
- Events - Event-driven architecture
Fundamentals
- The Basics
- Feature Sets
- Actions
- Variables
- Type System
- Control Flow
- Error Handling
- Computations
- Dates
- Concurrency
Runtime & Events
I/O & Communication
Advanced