Docs

Writing a part

A part is where the rules and the types live. It belongs to the system rather than to a component, so the same logic serves an api, a command-line client, or both, and it never learns which.

# systems/shop/src/models/parts/orders-core.toml
links.store = "couchbase/data"
parts       = []

The crate

# systems/shop/src/models/Cargo.toml
[package]
name    = "shop_models"
version = "0.1.0"
edition = "2024"

[dependencies]
bluetext = { workspace = true }
links    = { package = "shop_links", path = "../links" }

links is generated: b generate writes a module per declared link, so links::store exists because the part declared it.

The generated module hands the part a typed handle. What it does not hand it is a host, a port or a credential — those are resolved at deploy and arrive as configuration, which is why the same code runs against a container on your machine and a managed service in production.

// systems/shop/src/models/src/lib.rs
pub struct OrdersCore {
    couchbase: links::store::bluetext::Driver,
}

The handle's type and its methods come from the driver the linked interface declares. For a Couchbase link that means you are writing Couchbase calls, with its own vocabulary of buckets and documents — the driver is the contract, which is why the component is named for it.

What the part owns

The part owns the stored shape, so decisions about it belong here rather than in a handler: which bucket, what the documents look like, what ordering means. A handler translates between the part's types and the wire shapes its endpoint declared, and does nothing else.

That division is what makes a second consumer cheap. The command-line client calls the same api and gets the same answers, because the logic never knew a browser was involved.

Faults are not refusals

A part distinguishes two things a caller must tell apart: a refusal, which means the rule said no and retrying will not help, and a fault, which means the store did not answer and retrying might. Its error type carries both, and a handler maps them to the two channels its signature has — the declared refusal, and a panic for what is neither.