← Back to home

0. Introduction & notation

Loom is a declarative DSL for Domain-Driven Design. A .ddd source describes a system of bounded contexts — aggregates, value objects, events, repositories, projections, workflows, APIs, storage, and UI — and the toolchain compiles it into a runnable multi-project tree wired together as one docker compose stack. Five backends (TypeScript/Hono, .NET/ASP.NET, Phoenix LiveView on plain Ecto, Python/FastAPI, Java/Spring Boot) and six frontends (React, Vue, Svelte, Angular, Feliz F#/Fable, Flutter) consume the same source.

This reference documents the language surface, feature by feature. It is not a tutorial and not a generator internals guide — for those see ../language.md (prose tour) and ../technical.md (pipeline internals).

How to read it

The reference is non-sequential. Each chapter stands alone and cross-links the others. Two ways in:

The example convention

Every feature carries two examples: the Loom source you write and the target output it generates. The generated output appears in a tabbed picker — choose the platform you care about; your choice follows you across the page and across the reference.

Here is the convention itself, shown on a trivial aggregate. The same .ddd declaration, lowered on three of the five backends (pick any tab — the choice sticks):

context Catalog {
  aggregate Product {
    name: string
    price: money
  }
}
// db/schema.ts — the emitted Drizzle table (excerpt)
export const products = catalogSchema.table("products", {
  id: uuid("id").primaryKey(),
  name: text("name").notNull(),
  price: numeric("price", { precision: 19, scale: 4 }).notNull(),
  version: integer("version").notNull(),
});
// Domain/Products/Product.cs — the aggregate entity (excerpt)
public sealed class Product
{
    public ProductId Id { get; private set; }
    public string Name { get; private set; } = default!;
    public decimal Price { get; private set; } = default!;
    public int Version { get; private set; } = default!;
    // …
}
# app/db/schema.py — the SQLAlchemy row (excerpt)
class ProductRow(Base):
    __tablename__ = "products"
    id: Mapped[str] = mapped_column(Uuid(as_uuid=False), primary_key=True)
    name: Mapped[str] = mapped_column(Text)
    price: Mapped[Decimal] = mapped_column(Numeric(19, 4))
    version: Mapped[int] = mapped_column(Integer)

Every example in the reference — this one included — is sourced by actually running the generator, never hand-waved; see AUTHORING.md. (The version column is the optimistic- concurrency token every state-based aggregate carries.)

When a feature is backend-only (a repository query) or frontend-only (a page primitive), only the relevant tabs appear. When backends diverge — say TPH vs TPC table mapping — that divergence is the point, and the tabs sit side by side so you can compare.

Notation

The pipeline, in one breath

Every feature in this reference is a slice through the same ten-phase, strictly one-directional compiler. Knowing the shape helps you predict where a feature shows up in the output:

.ddd → ① parse → ② macro expand → ③ scope/link → ④ AST validate → ⑤ lower
     → ⑥ enrich → ⑦ IR validate → ⑧ per-platform codegen → ⑨ system compose → ⑩ write

The full walk-through is in ../technical.md. You do not need it to use the language — but it explains why the same .ddd produces structurally compatible output across five backends and six frontends.