Feature Sets
Feature sets are the primary organizational unit in ARO. They group related statements that work together to accomplish a business goal.
Defining Feature Sets
A feature set consists of a name, business activity, and body:
(Feature Name: Business Activity) {
(* statements *)
}
Naming Guidelines
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
Feature Set Categories
Application Lifecycle
These special feature sets manage the application lifecycle:
(* Entry point - exactly one per application *)
(Application-Start: My Application) {
<Log> "Starting..." to the <console>.
<Start> the <http-server> on port 8080.
<Return> an <OK: status> for the <startup>.
}
(* Called on graceful shutdown - optional *)
(Application-End: Success) {
<Stop> the <http-server>.
<Return> an <OK: status> for the <shutdown>.
}
HTTP Route Handlers (Contract-First)
Feature sets named after OpenAPI operationId values handle HTTP requests:
(* Feature sets named after operationIds in openapi.yaml *)
(listUsers: User API) {
<Retrieve> the <users> from the <user-repository>.
<Return> an <OK: status> with <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>.
}
(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>.
}
Event Handlers
Feature sets with "Handler" in the business activity respond to events:
(* Handle domain events *)
(Send Welcome Email: UserCreated Handler) {
<Extract> the <user> from the <event: user>.
<Extract> the <email> from the <user: email>.
<Send> the <welcome-email> to the <email>.
<Return> an <OK: status> for the <notification>.
}
(* 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>.
<Return> an <OK: status> for the <processing>.
}
Feature Set Execution
Triggering
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 OpenAPI operationId
- Events: Event handlers respond to emitted events
- Application shutdown:
Application-Endruns during shutdown
Execution Flow
Within a feature set, statements execute sequentially:
(Process Order: 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. Emit event *)
<Emit> an <OrderCreated: event> with <order>.
(* 6. Return response *)
<Return> a <Created: status> with <order>.
}
Early Returns
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>.
if <user> is empty then {
<Return> a <NotFound: status> for the <missing: user>.
}
<Return> an <OK: status> with <user>.
}
Organizing Feature Sets
By File
Organize related feature sets in files:
MyApp/
├── openapi.yaml # Required for HTTP server
├── main.aro # Application lifecycle
├── users.aro # User CRUD operations
├── orders.aro # Order management
├── payments.aro # Payment processing
├── notifications.aro # Email/SMS handlers
└── events.aro # Domain event handlers
Cross-File Communication
Publishing Variables
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>.
}
Emitting Events
Trigger other feature sets via events:
(* In orders.aro *)
(createOrder: 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>.
}
(* In notifications.aro - automatically triggered *)
(Send Confirmation: OrderCreated Handler) {
<Extract> the <order> from the <event: order>.
<Send> the <confirmation-email> to the <order: customerEmail>.
<Return> an <OK: status> for the <notification>.
}
Best Practices
Single Responsibility
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 *)
}
Consistent Structure
Follow a consistent pattern:
(Feature Name: 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>.
<Emit> an <Event: type> with <output>.
(* 4. Return *)
<Return> an <OK: status> with <output>.
}
Next Steps
Actions - Understanding and using actions
Application Lifecycle - Startup and shutdown