Projections
A projection is a read-shaped view over an entity — the fields and actions that a particular screen needs. Projections are how AUSUS turns a domain into something a UI can render without the UI knowing the domain.
Declaring a projection
A projection is declared on the entity, naming the fields and actions it exposes:
$dsl->entity('invoice')
->fields([ /* ... */ ])
->actions([ /* ... */ ])
->projection('summary',
fields: ['id', 'number', 'customer_name', 'status', 'amount'],
actions: ['create', 'cancel'],
role: 'invoice.viewer')
->projection('detail',
fields: ['id', 'number', 'customer_name', 'status', 'amount', 'issued_at', 'created_at', 'updated_at'],
actions: ['issue', 'cancel'],
role: 'invoice.viewer');
Each projection becomes a ProjectionNode in the graph with an FQN of
{entity}.{projection name}, e.g. billing.invoice.summary. The optional
role attaches a read policy.
Rendering a projection
ProjectionRenderer turns a projection into a ViewSchema
— a JSON-shaped description of fields, actions, and data:
use Ausus\Runtime\ProjectionRenderer;
$renderer = new ProjectionRenderer($graph, $driver, $tenant);
// List form — no subject:
$list = $renderer->render('billing.invoice.summary');
// $list['data']['items'] -> array of rows for the tenant
// Detail form — with a subject Reference:
$detail = $renderer->render('billing.invoice.detail', $invoiceRef);
// $detail['data']['item'] -> a single row
The shape of data tells the consumer which view to draw:
data.itemspresent → a list view.data.itempresent → a detail view.
The React renderer's ViewSchemaConsumer
dispatches on exactly this.
List vs detail
| List | Detail | |
|---|---|---|
| Call | render($fqn) | render($fqn, $subjectRef) |
data | { items: [...], pagination } | { item: {...} | null } |
| Typical actions | list-level (e.g. create) | item-level (e.g. issue, cancel) |
The renderer separates list actions (no subject required) from item
actions (subject required) automatically based on each action's
subjectRequired flag.
Current v0.1.0 limitations
- List rendering returns all rows for the tenant. There is no filtering,
sorting, or real pagination — the ViewSchema
filtersarray is empty andpagination.nextCursoris alwaysnull. - The list query path reads rows directly rather than through the
Repositorycontract (the v0.1.0Repositoryhas nofindMany). This is a known internal shortcut, documented as a v0.1.0 finding. - Field labels are derived mechanically from field names (
customer_name→ "Customer name"). There is no label localization or override in v0.1.0.
Related
- ViewSchema — the wire format a projection renders to.
- The React renderer — the client that consumes it.
- The HTTP API — serves projections over HTTP.