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 (
/ordersoperations 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
crudfacet 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.