Skip to content

Day 29 — Compose the Workspace and API

Today Mosaic assembles existing crates without turning them into one dependency cycle.

mosaic-api / forge-cli
mosaic-harness ─▶ mosaic-domain
↓ ↓
tools evidence
↓ ↓
storage / db / artifacts / models

Domain types do not import Axum, SQLx, Clap, or a provider SDK. Interface crates translate external requests into domain values and translate domain errors into HTTP or process results.

pub struct AppState {
pub runs: Arc<dyn RunService>,
}
async fn create_run(
State(state): State<AppState>,
Json(request): Json<CreateRunRequest>,
) -> Result<(StatusCode, Json<RunView>), ApiError> {
let command = request.try_into_domain()?;
let run = state.runs.create(command).await?;
Ok((StatusCode::ACCEPTED, Json(run.into())))
}

Validate body size, content type, identity, tenant, task schema, and idempotency key before expensive work. Return one stable error envelope; do not leak internal provider or database messages.

Terminal window
cd rust/rust-ai-engineering
cargo test -p mosaic-api
cargo run -q -p mosaic-api --example http_boundary

Use an in-process router test so status, headers, body, and shared state are exercised without binding a port.

Test malformed JSON, oversized body, unknown field, missing tenant, duplicate idempotency key, provider unavailable, and internal timeout. Assert stable external errors and detailed internal traces.

Trace one accepted request from HTTP JSON to domain command, durable run ID, and response. No provider wire type should cross the boundary.

Deep reference: Axum router, state, and errors.

Next: Day 30 — Durable Jobs →.