← Back to Documentation

Repositories

Repositories provide persistent in-memory storage that survives across HTTP requests and event handlers. Unlike regular variables which are scoped to a single feature set execution, repositories maintain state for the lifetime of the application.

Overview

In ARO, each HTTP request creates a fresh execution context. Variables defined in one request aren't available in another:

(* This won't work - count resets on each request *)
(GET /count: Counter API) {
    <Create> the <count> with 0.
    <Compute> the <new-count> from <count> + 1.
    <Return> an <OK: status> with <new-count>.
}

Repositories solve this by providing shared storage:

(POST /increment: Counter API) {
    <Retrieve> the <counts> from the <counter-repository>.
    <Compute> the <current> from <counts: length>.
    <Store> the <current> into the <counter-repository>.
    <Return> an <OK: status> with <current>.
}

(GET /count: Counter API) {
    <Retrieve> the <counts> from the <counter-repository>.
    <Compute> the <total> from <counts: length>.
    <Return> an <OK: status> with { count: <total> }.
}

Repository Naming Convention

Repository names must end with -repository. This is how ARO distinguishes repositories from regular variables:

(* These are repositories *)
<user-repository>
<message-repository>
<order-repository>
<session-repository>

(* These are NOT repositories - just regular variables *)
<users>
<messages>
<user-data>

The naming convention:

Storing Data

Use the <Store> action to save data to a repository:

<Store> the <data> into the <name-repository>.

Preposition Variants

All of these are equivalent:

<Store> the <user> into the <user-repository>.
<Store> the <user> in the <user-repository>.
<Store> the <user> to the <user-repository>.

Storage Semantics

Repositories use list-based storage. Each store operation appends to the list:

(* First request *)
<Store> the <user1> into the <user-repository>.
(* Repository: [user1] *)

(* Second request *)
<Store> the <user2> into the <user-repository>.
(* Repository: [user1, user2] *)

(* Third request *)
<Store> the <user3> into the <user-repository>.
(* Repository: [user1, user2, user3] *)

Example: Storing Messages

(postMessage: Chat API) {
    <Extract> the <data> from the <request: body>.
    <Extract> the <text> from the <data: message>.
    <Extract> the <author> from the <data: author>.

    <Create> the <message> with {
        text: <text>,
        author: <author>,
        timestamp: now
    }.

    <Store> the <message> into the <message-repository>.

    <Return> a <Created: status> with <message>.
}

Retrieving Data

Use the <Retrieve> action to fetch data from a repository:

<Retrieve> the <items> from the <name-repository>.

Return Value

Example: Retrieving All Messages

(getMessages: Chat API) {
    <Retrieve> the <messages> from the <message-repository>.
    <Return> an <OK: status> with { messages: <messages> }.
}

Filtered Retrieval

Use where to filter results:

(getUserById: User API) {
    <Extract> the <id> from the <pathParameters: id>.
    <Retrieve> the <user> from the <user-repository> where id = <id>.
    <Return> an <OK: status> with <user>.
}

Single Item Retrieval

Use specifiers to retrieve a single item from a repository:

(* Get the most recently stored item *)
<Retrieve> the <message> from the <message-repository: last>.

(* Get the first stored item *)
<Retrieve> the <message> from the <message-repository: first>.

(* Get by numeric index (0-based) *)
<Retrieve> the <message> from the <message-repository: 0>.

This is useful when you only need one item, like the latest message in a chat:

(getLatestMessage: Chat API) {
    <Retrieve> the <message> from the <message-repository: last>.
    <Return> an <OK: status> with { message: <message> }.
}

If the repository is empty, an empty string is returned.

Business Activity Scoping

Repositories are scoped to their business activity. Feature sets with the same business activity share repositories:

(* Same business activity: "Chat API" *)
(* These share the same <message-repository> *)

(postMessage: Chat API) {
    <Store> the <message> into the <message-repository>.
    <Return> a <Created: status>.
}

(getMessages: Chat API) {
    <Retrieve> the <messages> from the <message-repository>.
    <Return> an <OK: status> with <messages>.
}

(deleteMessage: Chat API) {
    (* Same repository as above *)
    <Retrieve> the <messages> from the <message-repository>.
    (* ... *)
}

Different Business Activities = Different Repositories

(* Business activity: "Chat API" *)
(postMessage: Chat API) {
    <Store> the <msg> into the <message-repository>.
}

(* Business activity: "Admin API" - DIFFERENT repository! *)
(postAuditLog: Admin API) {
    (* This <message-repository> is separate from Chat API's *)
    <Store> the <log> into the <message-repository>.
}

This scoping:

Complete Example: Simple Chat Application

main.aro

(Application-Start: Simple Chat) {
    <Log> the <startup: message> for the <console> with "Starting Simple Chat...".
    <Start> the <http-server> for the <contract>.
    <Keepalive> the <application> for the <events>.
    <Return> an <OK: status> for the <startup>.
}

api.aro

(* GET /status - Return the last message *)
(getStatus: Simple Chat API) {
    <Retrieve> the <message> from the <message-repository: last>.
    <Return> an <OK: status> with { message: <message> }.
}

(* POST /status - Store a new message *)
(postStatus: Simple Chat API) {
    <Extract> the <message> from the <body: message>.

    <Store> the <message> into the <message-repository>.

    <Return> a <Created: status> with { message: <message> }.
}

Testing

# Post a message
curl -X POST http://localhost:8080/status \
  -H 'Content-Type: application/json' \
  -d '{"message":"Hello!"}'
# Response: {"message":"Hello!"}

# Post another message
curl -X POST http://localhost:8080/status \
  -H 'Content-Type: application/json' \
  -d '{"message":"World!"}'
# Response: {"message":"World!"}

# Get the last message
curl http://localhost:8080/status
# Response: {"message":"World!"}

Lifetime and Persistence

Application Lifetime

Repositories persist for the lifetime of the application:

No Disk Persistence

Repositories are in-memory only:

For persistent storage, use a database integration (future ARO feature).

Best Practices

Use Descriptive Repository Names

(* Good - clear what's stored *)
<user-repository>
<pending-order-repository>
<session-token-repository>

(* Avoid - too generic *)
<data-repository>
<stuff-repository>

One Repository Per Domain Concept

(* Good - separate repositories for different concepts *)
<user-repository>
<order-repository>
<product-repository>

(* Avoid - mixing concepts *)
<everything-repository>

Keep Repository Data Simple

Store simple, serializable data:

(* Good - simple object *)
<Create> the <user> with {
    id: <id>,
    name: <name>,
    email: <email>
}.
<Store> the <user> into the <user-repository>.

(* Avoid - complex nested structures *)
<Store> the <entire-request-context> into the <debug-repository>.

Next Steps