11. The Standard Library
Up to this point we’ve used only the bare language: module, facet, interface. Three keywords. Every example has been a labeled box with operations and arrows.
That’s enough to model anything structurally, but it’s missing two things real projects need:
- Visual distinction. A database, a service, a user, and an external vendor should look different in a diagram. With only
module, they don’t. - Enforced conventions. Every service in this org has a team owner. The bare
modulekind doesn’t enforce anything; the validator can’t tell a service from an external client.
The standard library solves both. It ships two packages of kinds — arch.modules and arch.kinds — that introduce semantic subtypes of the base kinds. Each one cascades a default widget and (usually) requires a few fields.
This chapter introduces what’s in the stdlib and how to bring it into your project.
Bringing the stdlib into scope
Add two lines to your package.archspace:
name: my-project
use * from arch.modulesuse * from arch.kindsarch.modules adds module subtypes (service, database, actor, …). arch.kinds adds interface subtypes (command, query, event, …). Both packages auto-resolve against the toolchain’s bundled stdlib — no dependencies entry needed.
Now your .arch files can use the richer kinds:
service Payments { team: Payments labels { domain: Payments }
command Authorize event PaymentEvents}The kinds you import are subtypes of module and interface. Every rule from chapters 4 and 5 applies — they just add defaults and requirements on top.
Module kinds (arch.modules)
Seven kinds ship out of the box.
| Kind | Required blanks | Defaults | Default widget |
|---|---|---|---|
service | required cascade team, required labels.domain | — | arch-service |
frontend | required cascade team, required labels.domain | — | arch-frontend |
database | required cascade team, required labels.data.classification | — | arch-database |
component | — | cascade team (default empty) | arch-component |
system | — | cascade team | arch-system |
external_system | required ext.vendor, required ext.contract.url | — | arch-external-system |
actor | — | — | arch-actor |
The columns:
- Required blanks must be filled or dropped by every instance. Forgetting them is a parse error.
- Defaults are pre-set fields the instance inherits; the cascade flows them to nested modules.
- Default widget is the custom-element tag the renderer mounts. Every kind has its own visual identity.
use * from arch.modules gives you working visuals for free; no widget script of your own required. (Chapter 20 covers widget customization.)
When to use which:
service— a backend service with a maintained team.frontend— a user-facing app (web, mobile).database— a datastore.component— a finer-grained piece inside another module. Often nested.system— a container grouping other modules (a subsystem or product area).external_system— a vendor or partner you don’t own.ext.vendorandext.contract.urlare required so external dependencies always document what they are and where their contract lives.actor— a person or external client originating calls. Actors don’t expose operations; they call them.
Use the bare module kind when none of these fit — usually rare. When you find yourself wanting “almost service but with three more required fields,” define a project-local subtype (Chapter 16).
Interface kinds (arch.kinds)
Six kinds ship.
| Kind | Parent | Semantic |
|---|---|---|
sync | interface | Generic synchronous interface |
async | interface | Generic asynchronous interface |
command | sync | State-changing operation |
query | sync | Read-only operation |
event | async | Pub/sub broadcast |
stream | async | Long-lived data stream |
The two parent kinds (sync, async) drive edge styling: sync edges solid, async edges dashed. The four child kinds (command, query, event, stream) inherit that styling and convey the specific semantic.
Only event and stream (or anything subtyping them) are meaningful targets for subscribes: wiring. The validator rejects subscribes: someCommand.
Bare vs. stdlib: the same model
The same architecture written two ways:
Bare:
module Payments { team: Payments interface Authorize interface OrderEvents}
module Shipping { team: Fulfillment interface CreateShipment { subscribes: Payments.OrderEvents }}Stdlib:
service Payments { team: Payments labels { domain: Payments }
command Authorize event OrderEvents}
service Shipping { team: Fulfillment labels { domain: Fulfillment }
command CreateShipment { subscribes: Payments.OrderEvents }}The structure is identical — two modules, three interfaces, one subscribe wire. The stdlib version adds:
servicewidgets (specific visual identity).command/eventinterface kinds (sync vs async edge styling).- The
domainlabel (now required because the type demands it).
The bare version is valid; the stdlib version is what real projects ship. The translation is mechanical.
When NOT to use the stdlib
A few scenarios where bare kinds make sense:
- Learning the language. Chapters 2-10 used bare kinds for exactly this reason — fewer moving parts to hold in your head.
- Tiny scratch projects without a manifest. Anonymous packages can’t
useanything, so bare kinds are all you get. - Building your own toolchain. If you’re integrating Archlang into a system that already defines its own ontology, you may want to bypass the stdlib’s vocabulary entirely.
Most projects use the stdlib.
What’s next
Chapter 12: Packages → — the manifest in depth: dependencies, version, exporting types.