Build a cooperating service

Now combine the resource lifecycle, an active policy and internal coordination. The delivery service exposes requests through HTTP while two courier agents carry out simulated work. The agents share one service process; the couriers do not need their own HTTP bodies.

Read the contract before the code

Download the delivery service. Clients submit orderId and destination. The coordinator owns status, courier, detail and history. The collection permits creation and reading, while external replacement and deletion are refused.

The lifecycle is:

created → submitted → assigned → in_transit → retrying → delivered or failed

created describes the HTTP resource outcome; the remaining stages are published business state. The simulation intentionally tries a blocked route to make recovery visible.

Follow one successful request

Stop the greeting service, extract the delivery project and run mvn clean compile astra:run. Submit:

curl -i -X POST -H 'Accept: application/json' -H 'Content-Type: application/json' \
  -H 'Slug: lesson-1' -d '{"orderId":"lesson-1","destination":"library"}' \
  http://localhost:9000/main/deliveries

Read the resource at the returned location. Its terminal result reports delivered, with courier-1 and Arrived via park. The history records the retry even if the simulation finishes before your first GET. Follow the detailed delivery walkthrough for the full response.

The coordinator handles creation, chooses the worker and stores assigned(ResourceName, Worker). It accepts progress only from that worker for that resource. The resource name is the correlation identifier; a client-provided order ID is not an idempotency key in this implementation.

Keep acknowledgement and completion separate

For each progress message the coordinator publishes the new state, then acknowledges that publication. The courier waits for the acknowledgement before sending the next stage. This makes the teaching example’s history deterministic.

The publication plan uses a synchronization lock around its read-and-replace operation. That protects updates made through that coordinator plan; it is not a transaction across arbitrary HTTP clients or other agents. The active request policy separately excludes external writers.

The courier’s acknowledgement wait has no timeout. A stopped coordinator could leave it suspended. The example demonstrates ordering, but a production coordination contract would also specify acknowledgement deadlines and an explicit recovery path.

Exercise failure before adding complexity

Submit a fresh request for island. There is no usable route, so the request reaches failed. Then try an empty order identifier: validation fails before courier assignment. Finally try PUT to forge completion: the resource refuses it with 403.

These exercises expose three different boundaries: business execution, input validation and client permissions. A test that only checks 201 would miss all three.

Choose the next extension

Add courier capacity or an assignment policy only after you can explain each terminal outcome. For persistence, specify what a restart does to requests and live assignments. For retries, specify how duplicate messages or submissions are recognized. For notifications, define the relationship between published resource state and a WebSub update.

The sum and ping-pong projects provide smaller demonstrations of reacting to resource creation. They are alternatives for practicing one mechanism, rather than prerequisites to this application.

Next: Test and deploy.

Find complete downloads and larger applications in the shared MAMS examples collection.

Further Details