-
Notifications
You must be signed in to change notification settings - Fork 0
Guide Feature Sets
Feature sets are the primary organizational unit in ARO. They group related statements that work together to accomplish a business goal.
A feature set consists of a name, business activity, and body:
(Feature Name: Business Activity) {
(* statements *)
}
Feature Set Name - Describes what the feature does:
- Use action-oriented names:
Create User,Calculate Total,Send Notification - Be specific:
Validate Email Formatnot justValidate - Use business terminology:
Process Refund,Approve Order
Business Activity - Describes the domain context:
- Use noun phrases:
User Management,Order Processing - Group related features: All user features might use
User API - Be consistent across your application
(Create User Account: User Management) { ... }
(Validate User Credentials: Authentication) { ... }
(Process Payment: Order Processing) { ... }
(Send Order Confirmation: Notifications) { ... }
(Calculate Shipping Cost: Shipping) { ... }
These special feature sets manage the application lifecycle:
(* Entry point - exactly one per application *)
(Application-Start: My Application) {
Log "Starting..." to the <console>.
Keepalive the <application> for the <events>.
Return an <OK: status> for the <startup>.
}
(* Called on graceful shutdown - optional, at most one *)
(Application-End: Success) {
Log "Shutting down..." to the <console>.
Return an <OK: status> for the <shutdown>.
}
(* Called on error - optional, at most one *)
(Application-End: Error) {
Extract the <error> from the <shutdown: error>.
Log <error> to the <console>.
Return an <OK: status> for the <error-handling>.
}
ARO uses contract-first HTTP development. Routes are defined in openapi.yaml, and feature sets are named after operationId values:
openapi.yaml:
openapi: 3.0.3
info:
title: User API
version: 1.0.0
paths:
/users:
get:
operationId: listUsers
post:
operationId: createUser
/users/{id}:
get:
operationId: getUser
put:
operationId: updateUser
delete:
operationId: deleteUserhandlers.aro:
(* GET /users *)
(listUsers: User API) {
Retrieve the <users> from the <user-repository>.
Return an <OK: status> with <users>.
}
(* POST /users *)
(createUser: User API) {
Extract the <user-data> from the <request: body>.
Create the <user> with <user-data>.
Store the <user> into the <user-repository>.
Return a <Created: status> with <user>.
}
(* GET /users/{id} *)
(getUser: User API) {
Extract the <user-id> from the <pathParameters: id>.
Retrieve the <user> from the <user-repository> where id = <user-id>.
Return an <OK: status> with <user>.
}
(* PUT /users/{id} *)
(updateUser: User API) {
Extract the <user-id> from the <pathParameters: id>.
Extract the <updates> from the <request: body>.
Retrieve the <user> from the <user-repository> where id = <user-id>.
Transform the <updated-user> from the <user> with <updates>.
Store the <updated-user> into the <user-repository>.
Return an <OK: status> with <updated-user>.
}
(* DELETE /users/{id} *)
(deleteUser: User API) {
Extract the <user-id> from the <pathParameters: id>.
Delete the <user> from the <user-repository> where id = <user-id>.
Return a <NoContent: status> for the <deletion>.
}
Access path parameters via <pathParameters: name>:
(* For /users/{userId}/orders/{orderId} *)
(getUserOrder: Order API) {
Extract the <user-id> from the <pathParameters: userId>.
Extract the <order-id> from the <pathParameters: orderId>.
Retrieve the <order> from the <order-repository>
where userId = <user-id> and id = <order-id>.
Return an <OK: status> with <order>.
}
Feature sets with "Handler" in the business activity respond to events:
(* Handle file system events *)
(Process Upload: FileCreated Handler) {
Extract the <path> from the <event: path>.
Read the <content> from the <file: path>.
Transform the <processed> from the <content>.
Store the <processed> into the <processed-repository>.
Return an <OK: status> for the <processing>.
}
(* Handle socket events *)
(Echo Data: DataReceived Handler) {
Extract the <data> from the <event: data>.
Extract the <connection> from the <event: connection>.
Send the <data> to the <connection>.
Return an <OK: status> for the <echo>.
}
Feature sets are never called directly. They're triggered by:
-
Application start:
Application-Startruns once at startup - HTTP requests: Route handlers match incoming requests via operationId
- Events: Event handlers respond to file/socket events
-
Application shutdown:
Application-Endruns during shutdown
Within a feature set, statements execute sequentially:
(createOrder: Order Management) {
(* 1. Extract data *)
Extract the <order-data> from the <request: body>.
(* 2. Validate *)
Validate the <order-data> for the <order-schema>.
(* 3. Create order *)
Create the <order> with <order-data>.
(* 4. Store *)
Store the <order> into the <order-repository>.
(* 5. Return response *)
Return a <Created: status> with <order>.
}
Use control flow to return early:
(getUser: User API) {
Extract the <user-id> from the <pathParameters: id>.
Retrieve the <user> from the <user-repository> where id = <user-id>.
Return a <NotFound: status> for the <missing: user> when <user> is empty.
Return an <OK: status> with <user>.
}
Organize related feature sets in files:
MyApp/
├── openapi.yaml # API contract (required for HTTP)
├── main.aro # Application lifecycle
├── users.aro # User CRUD operations
├── orders.aro # Order management
├── payments.aro # Payment processing
└── events.aro # Event handlers
Group by business domain:
users.aro:
(listUsers: User API) { ... }
(createUser: User API) { ... }
(getUser: User API) { ... }
(updateUser: User API) { ... }
(deleteUser: User API) { ... }
orders.aro:
(listOrders: Order API) { ... }
(createOrder: Order API) { ... }
(getOrder: Order API) { ... }
(updateOrderStatus: Order API) { ... }
Group by technical concern:
events.aro:
(Log User Activity: FileCreated Handler) { ... }
(Process Upload: FileCreated Handler) { ... }
(Echo Data: DataReceived Handler) { ... }
Make variables available to other feature sets:
(* In config.aro *)
(Load Configuration: 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 *)
(getSettings: Settings API) {
Extract the <timeout> from the <app-config: timeout>.
Return an <OK: status> with <timeout>.
}
Published symbols are automatically evicted when the publishing feature set completes — except for symbols published from Application-Start or Application-End, which persist for the process lifetime.
| Published from | Lifetime |
|---|---|
Application-Start |
Entire process |
Application-End |
Entire process |
| HTTP handler / event handler / any other | Until that execution ends |
This means you can safely Publish as inside request handlers without worrying about memory accumulation. Values from the current request are cleaned up when it completes, and a fresh execution of the same handler publishes fresh values.
Recommendation: Publish long-lived configuration from Application-Start. For values that only matter during one request or handler invocation, local variables are sufficient and simpler.
Each feature set should do one thing well:
(* Good - focused on one task *)
(Validate Email Format: Validation) {
Extract the <email> from the <input: email>.
Validate the <email> for the <email-pattern>.
Return an <OK: status> for the <validation>.
}
(* Avoid - too many responsibilities *)
(Handle User: User Management) {
(* Don't mix validation, creation, notification, and logging *)
}
Names should describe the action and context:
(* Good - clear and specific *)
(Send Password Reset Email: Authentication) { ... }
(Calculate Order Subtotal: Pricing) { ... }
(Validate Credit Card Number: Payment Validation) { ... }
(* Avoid - vague or unclear *)
(Do Stuff: Things) { ... }
(Process: Handler) { ... }
Follow a consistent pattern:
(featureName: Domain) {
(* 1. Extract/validate inputs *)
Extract the <input> from the <source>.
Validate the <input> for the <schema>.
(* 2. Business logic *)
Create the <result> with <input>.
Transform the <output> from the <result>.
(* 3. Side effects *)
Store the <output> into the <repository>.
(* 4. Return *)
Return an <OK: status> with <output>.
}
- Actions - Understanding and using actions
- Variables and Data Flow - Variable binding and scoping
- Events - Event-driven programming
Fundamentals
- The Basics
- Feature Sets
- Actions
- Variables
- Type System
- Control Flow
- Error Handling
- Computations
- Dates
- Concurrency
Runtime & Events
I/O & Communication
Advanced