Data Schemas as Contracts

Component Content Architecture separates content from components (see Component Content Architecture). For structured content — a roster of people, a catalog of products, a list of events — a data schema is the contract across that boundary. It says what a person is, independently of any component that renders one or any file that stores one.

The Data Schemas guide covers the how — authoring, sharing, registering. This page covers the why: why the schema is a single declaration, why it's addressed by name, and why that shape is what makes content types reusable.


One definition, three jobs

A schema earns its keep because it is consumed three different ways — and in CCA they all read the same declaration:

The schema declaresConsumerWhat it does with it
Field names, types, requiredness, allowed valuesuniweb validateFlags data that doesn't fit before it ships — a misspelled field, a value outside an enum
The delivery shapeThe runtimeHands each record to components as content.data.<key>, as the record holds it — a field it lacks is absent, and the component decides what that shows
The same fields, as editable inputsThe editorRenders the form a content author or client fills in — the schema is the spec for the editing UI

In a conventional stack these are three artifacts maintained separately: a validation schema, a form configuration, and a fetch-and-serialize layer. They drift — a field added to the form but not the validator, a default that lives in the API but not the UI. CCA collapses them into one declaration, so drift isn't something you manage; it's something that can't happen. Add a field once and validate starts checking it, the runtime starts delivering it, and the form gains an input.

This is the same bargain CCA makes everywhere: a constraint (one declaration, not three) that turns out to be less work and a tighter design.


Identity is the name

A schema is addressed by an org-scoped name — @std/person, @acme/product — not by a file path, a database id, or an import. The name is the whole contract. The definition behind it resolves locally during development (from a package or a folder on disk) and from the Uniweb registry once registered, but the reference never changes.

That indirection is what makes the schema shareable rather than copied:

  • A foundation writes @/product in its source and never hard-codes its own org. The same foundation can be registered under any scope — the name resolves relative to where it's registered.
  • A reference to @std/person, or to a schema another organization registered, is a name the registry resolves. The foundation declares what shape it renders, not where the definition lives.

Because identity is the name, you register a schema by declaring it under a scope you own — not by minting an id or managing a version yourself. Re-declaring an unchanged schema is a no-op, and the producer never tracks versions. The name is the contract: once a backend holds records of a schema, a change that would invalidate them is refused there, and the new shape takes a new name (@acme/product-v2).


Content types are shared, not owned

A foundation renders content types; it doesn't own them. The same @std/person is rendered by a marketing foundation's team grid, an academic foundation's faculty directory, and a conference foundation's speaker list. Define person once; every foundation that needs people references it.

This is why the standards exist. @std/* is a small set of common shapes — person, event, article, project, publication, opportunity — maintained centrally so that the most frequent content types are defined well, once. An organization maintains its own shared types the same way: an @org/schemas repository is the single source of truth for a team's shapes, referenced by every foundation they build and registered so those shapes exist as managed content types.

The payoff compounds across a portfolio. Fix person in one place and every foundation that renders people inherits the fix. A client whose content is shaped by @acme/product can move between foundations — a different layout, a different visual identity — without re-entering or reshaping their content, because the content type is independent of the thing rendering it.


Where this sits on the portability spectrum

Foundations range from bundled (coupled to one site, fetching their own data, hardcoding their own shapes) to portable (the site supplies data, theme, and configuration). Naming content types by schema is the portable end of the data axis: a portable foundation declares the shapes it renders (@std/person, @/product) but not where the data comes from. The site — or the registry, for managed content — supplies the records; the runtime delivers them under the schema's key.

That separation is what lets one foundation serve many sites with different content and different data sources, and what lets a content author's structured data outlive any single foundation that renders it. The schema is the stable contract in the middle; everything on either side of it can change.


See Also