-
Notifications
You must be signed in to change notification settings - Fork 0
Guide Variables
Variables in ARO hold values during execution. This chapter explains how variables are created, scoped, and how data flows through your application.
Variables are implicitly declared when they appear as the result of an action:
Extract the <user-id> from the <request: parameters>.
(* user-id is now declared and bound *)
Create the <user> with <user-data>.
(* user is now declared and bound *)
There's no separate declaration step - variables come into existence when first assigned.
Variables use angle brackets:
<user> (* Simple variable *)
<user-id> (* Hyphenated name *)
<orderTotal> (* Camel case - works but not recommended *)
Naming Conventions:
- Use lowercase with hyphens:
<user-id>,<order-total> - Be descriptive:
<customer-email>not<ce> - Use nouns:
<user>,<order>,<payment>
Add qualifiers to access properties:
<user: id> (* The id property of user *)
<user: email> (* The email property *)
<order: lineItems> (* The lineItems property *)
<request: body> (* The body of request *)
Qualifiers can be chained conceptually, but typically you extract step by step:
Extract the <order> from the <request: body>.
Extract the <items> from the <order: lineItems>.
Extract the <first-item> from the <items: first>.
Variables are scoped to their feature set:
(GET /users/{id}: User API) {
Extract the <user-id> from the <request: parameters>.
(* user-id is available here *)
Retrieve the <user> from the <repository> where id = <user-id>.
(* user is available here *)
Return an <OK: status> with <user>.
}
(* user-id and user are NOT available outside this feature set *)
Variables don't persist across feature set executions:
(GET /count: Counter API) {
(* This won't work - count doesn't persist *)
Set the <count> to <count> + 1.
Return an <OK: status> with <count>.
}
Use repositories for persistence:
(GET /count: Counter API) {
Retrieve the <counter> from the <counter-repository>.
Compute the <new-count> from <counter: value> + 1.
Store the <counter> with { value: <new-count> } into the <counter-repository>.
Return an <OK: status> with <new-count>.
}
Use Publish to make variables available across feature sets:
(* In config.aro *)
(Load Config: Initialization) {
Read the <config: JSON> from the <file: "./config.json">.
Publish as <app-config> <config>.
Return an <OK: status> for the <loading>.
}
(* In any other file *)
(GET /settings: Settings API) {
(* Access the published variable *)
Extract the <port> from the <app-config: port>.
Return an <OK: status> with <port>.
}
- Publish configuration, not request-specific data
- Use descriptive aliases:
<app-config>,<database-pool> - Publish during initialization, not during request handling
(* Good - publish configuration once *)
(Application-Start: My App) {
Read the <config> from the <file: "./config.json">.
Publish as <app-config> <config>.
Return an <OK: status> for the <startup>.
}
(* Avoid - don't publish request data *)
(POST /users: User API) {
Create the <user> with <data>.
(* Don't do this - user is request-specific *)
Publish as <current-user> <user>.
}
Data flows through statements sequentially:
(Process Order: Order Management) {
(* Input *)
Extract the <order-data> from the <request: body>.
(* Validation *)
Validate the <order-data> for the <order-schema>.
(* Transformation *)
Transform the <order> from the <order-data>.
(* Enrichment *)
Compute the <total> for the <order: items>.
Transform the <enriched-order> from the <order> with { total: <total> }.
(* Persistence *)
Store the <enriched-order> into the <order-repository>.
(* Output *)
Return a <Created: status> with <enriched-order>.
}
Data can flow through different paths:
(GET /users/{id}: User API) {
Extract the <user-id> from the <request: parameters>.
Retrieve the <user> from the <repository> where id = <user-id>.
(* Error path *)
Return a <NotFound: status> for the <missing: user> when <user> is empty.
(* Success path *)
Transform the <user-response> from the <user>.
Return an <OK: status> with <user-response>.
}
Data flows between feature sets via events:
(* Source - emits event with data *)
(POST /orders: Order API) {
Create the <order> with <order-data>.
Store the <order> into the <order-repository>.
Emit an <OrderCreated: event> with <order>.
Return a <Created: status> with <order>.
}
(* Target - receives data from event *)
(Send Confirmation: OrderCreated Handler) {
Extract the <order> from the <event: order>.
Extract the <email> from the <order: customerEmail>.
Send the <confirmation> to the <email>.
Return an <OK: status> for the <notification>.
}
Provide type hints for clarity:
Read the <config: JSON> from the <file: "./config.json">.
Read the <image: bytes> from the <file: "./logo.png">.
Transform the <users: List> from the <response: data>.
| Type | Usage |
|---|---|
JSON |
JSON data |
bytes |
Binary data |
List |
Array/collection |
String |
Text data |
Number |
Numeric data |
Boolean |
True/false |
Date |
Date/time |
Available in HTTP handlers:
<request: method> (* HTTP method: GET, POST, etc. *)
<request: path> (* Request path *)
<request: parameters> (* Path parameters *)
<request: query> (* Query string parameters *)
<request: headers> (* HTTP headers *)
<request: body> (* Request body *)
Available in event handlers:
<event: type> (* Event type name *)
<event: timestamp> (* When event occurred *)
<event: ...> (* Event-specific data *)
Available in Application-End:
<shutdown: reason> (* Why shutting down *)
<shutdown: code> (* Exit code *)
<shutdown: signal> (* Signal name if applicable *)
<shutdown: error> (* Error if error shutdown *)
Once bound, a variable's value doesn't change:
Extract the <user-id> from the <request>.
(* user-id is now "123" - it won't change *)
(* To "modify", create a new variable *)
Transform the <new-value> from the <user-id>.
Use Transform with with to create modified copies:
Retrieve the <user> from the <repository> where id = <id>.
(* user is { name: "John", status: "pending" } *)
Transform the <active-user> from the <user> with { status: "active" }.
(* active-user is { name: "John", status: "active" } *)
(* user is still { name: "John", status: "pending" } *)
Repositories are special variables that persist across HTTP requests and event handlers within the same business activity. They provide in-memory storage for application state.
Repository names must end with -repository:
<message-repository> (* Valid repository *)
<user-repository> (* Valid repository *)
<order-repository> (* Valid repository *)
<messages> (* NOT a repository - regular variable *)
Use Store to save data to a repository:
(POST /messages: Chat API) {
Extract the <message> from the <request: body>.
Store the <message> into the <message-repository>.
Return a <Created: status> with <message>.
}
Data is appended to the repository as a list.
Use Retrieve to fetch data from a repository:
(GET /messages: Chat API) {
Retrieve the <messages> from the <message-repository>.
Return an <OK: status> with <messages>.
}
Returns all items in the repository, or an empty list if empty.
Repositories are scoped to their business activity:
(* Both share "Chat API" - same repository *)
(postMessage: Chat API) {
Store the <msg> into the <message-repository>.
}
(getMessages: Chat API) {
Retrieve the <msgs> from the <message-repository>.
}
(* Different activity - different repository! *)
(logError: Logging API) {
(* This is a separate message-repository *)
Store the <error> into the <message-repository>.
}
For more details, see Repositories.
(* Good *)
Extract the <customer-email> from the <order: customerEmail>.
Retrieve the <pending-orders> from the <repository> where status = "pending".
(* Avoid *)
Extract the <e> from the <o: customerEmail>.
Retrieve the <x> from the <repository> where status = "pending".
Each variable should have one purpose:
(* Good - clear purpose *)
Retrieve the <user> from the <repository>.
Transform the <user-dto> from the <user>.
Return an <OK: status> with <user-dto>.
(* Avoid - reusing variable name *)
Retrieve the <data> from the <repository>.
Transform the <data> from the <data>. (* Confusing *)
Only create variables you need:
(* Good - minimal variables *)
(GET /users/{id}: User API) {
Extract the <id> from the <request: parameters>.
Retrieve the <user> from the <repository> where id = <id>.
Return an <OK: status> with <user>.
}
(* Avoid - unnecessary intermediate variables *)
(GET /users/{id}: User API) {
Extract the <params> from the <request: parameters>.
Extract the <id> from the <params: id>.
Retrieve the <result> from the <repository> where id = <id>.
Extract the <user> from the <result>.
Return an <OK: status> with <user>.
}
- Control Flow - Conditional logic
- Events - Event-driven data flow
- Feature Sets - Organizing code
Fundamentals
- The Basics
- Feature Sets
- Actions
- Variables
- Type System
- Control Flow
- Error Handling
- Computations
- Dates
- Concurrency
Runtime & Events
I/O & Communication
Advanced