Skip to content

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 module kind 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.modules
use * from arch.kinds

arch.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.

KindRequired blanksDefaultsDefault widget
servicerequired cascade team, required labels.domainarch-service
frontendrequired cascade team, required labels.domainarch-frontend
databaserequired cascade team, required labels.data.classificationarch-database
componentcascade team (default empty)arch-component
systemcascade teamarch-system
external_systemrequired ext.vendor, required ext.contract.urlarch-external-system
actorarch-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.vendor and ext.contract.url are 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.

KindParentSemantic
syncinterfaceGeneric synchronous interface
asyncinterfaceGeneric asynchronous interface
commandsyncState-changing operation
querysyncRead-only operation
eventasyncPub/sub broadcast
streamasyncLong-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:

  • service widgets (specific visual identity).
  • command / event interface kinds (sync vs async edge styling).
  • The domain label (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 use anything, 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.