Skip to content

6. Facets

A module with twenty interfaces in a flat list is unreadable. A facet is a named grouping of interfaces (and nested facets) inside a module’s body. It’s organizational only — facets have no deployment semantics, no stable IDs, no presence outside the module they live in.

module Orders {
team: Commerce
facet OrdersResource {
"Order CRUD operations"
base: "/orders"
interface Create
interface Get
interface Update
interface Delete
facet Products {
base: "/products"
interface Add
interface Remove
}
}
facet Webhooks {
interface Subscribe
interface Unsubscribe
}
}

Two top-level facets on Orders: OrdersResource (with its own nested Products facet) and Webhooks. Each groups its interfaces under a name; each can carry fields, labels, and a description.

Why facets exist

Without facets, every interface lives directly in the module body. That works for small modules. For modules with a real public surface — an HTTP service exposing twenty endpoints, a domain service grouping operations by aggregate — the flat layout becomes noise.

Facets give you a place to:

  • Group conceptually related interfaces (/orders operations together).
  • Attach shared fields like a base path that should apply to everything below.
  • Apply a type template (Chapter 16) so you can stamp a crud facet that gets a fixed set of operations for free.

They aren’t about deployment, ownership, or addressing. Those belong on modules.

Containment is one-way

Rule. Modules contain facets; facets do not contain modules.

A facet can contain other facets and interfaces. It cannot contain a module. If you want a sub-module inside a service, declare it as a nested module (Chapter 4), not as a facet:

module Orders {
facet API {
interface Create // ✅ interfaces inside a facet
}
module Worker { // ✅ nested module — NOT inside the facet
interface Process
}
// ❌ This would be a parse error:
// facet Bad {
// module Inside { ... }
// }
}

The reason is identity. Modules carry stable IDs and represent architectural elements; facets are organizational. Allowing modules inside facets would muddle the two.

Dot-paths

Process steps and cross-references resolve through facets via dot-paths:

process Buy {
Customer > Orders.OrdersResource.Create
Customer > Orders.OrdersResource.Products.Add
Customer > Orders.Webhooks.Subscribe
}

The full path is Module.Facet.[Facet.]Interface. Facets are transparent to the resolver — they organize the source but don’t introduce a separate namespace you have to navigate around.

Facet kinds

facet is the only facet kind the stdlib defines. You can declare custom facet kinds when domain-specific vocabulary is preferred:

type facet resource {
"An HTTP resource — appends to a base path"
append base
}
module Orders {
resource OrdersResource {
base: "/orders"
interface Post
interface Get
}
}

resource is now a user-defined facet kind that behaves like facet plus an append base field. (Chapter 16 covers defining kinds; Chapter 18 covers append.)

Common conventions you’ll see in real projects:

  • resource — HTTP-resource style, appends paths.
  • capability — capability-style grouping, no path semantics.
  • endpoint_group — grouping of related interfaces under a shared label.

None of these ship with the stdlib; teams define them as needed.

Fields, labels, descriptions

Facet bodies carry the same field-and-label content as modules:

facet OrdersResource {
"Order CRUD operations"
base: "/orders"
version: v2
labels {
domain: Orders
}
interface Create
interface Get
}

Labels declared on a facet cascade to its contained interfaces and nested facets — that’s how a labels { domain: Orders } on the facet propagates to every operation inside without repetition. Chapter 18 covers cascade in full.

No stable IDs

Facets, like interfaces, don’t carry stable IDs. Their identity is the dot-path inside the enclosing module. Rename detection uses structural heuristics, same as interfaces (Chapter 13).

Empty bodies

A facet with nothing to add over its type’s template can omit the body:

module Orders {
resource OrdersResource // facet using a 'resource' kind, no instance additions
}

That declares an OrdersResource facet whose contents come entirely from the resource type. We’ll meet types in Chapter 15.

Summary

  • A facet groups interfaces (and nested facets) inside a module body.
  • Facets are organizational: no stable IDs, no deployment semantics.
  • Containment is one-way — modules contain facets, never the reverse.
  • Process steps reach interfaces inside facets by dot-path (Module.Facet.Interface).
  • Custom facet kinds (resource, capability) let you align with domain vocabulary.

What’s next

Chapter 7: Processes → — how behavior is described, and where dependency arrows actually come from.