Skip to content

Commit 6271a8e

Browse files
committed
Improve clarity of spec and docs prose
- Rewrite Static/Dynamic Composition definitions to mirror each other and say explicitly what VDP does and does not describe - Explain the recursion in the abstract instead of asserting it - State the concrete cost of hardcoded template choices in the problem statement - Fix the Section 5.4 resolution example: relative refs without a leading slash resolve under /api/ per RFC 3986, so use root-relative paths and note the difference - Align Section 13.1 prose with its example (VDP-Support/VDP-Version headers, not a token in Allow) - Fix multi-view example use case to match its dashboard templates - Smaller fixes: parallel bullet lists, resolution algorithm recursion wording, homepage phrasing
1 parent 98b1209 commit 6271a8e

3 files changed

Lines changed: 32 additions & 30 deletions

File tree

‎docs/examples.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ View-Template: https://github.com/SiteNetSoft/quarkus-pha/templates/components/f
2828

2929
## Composed View Descriptor
3030

31-
A layout template with nested slots, forming a template tree. The sidebar layout has two slots: `sidebarNav` for navigation, and `mainContent` for a dashboard that itself contains three further slots.
31+
A layout template with nested slots, forming a template tree. The sidebar layout has two slots: `sidebarNav` for navigation and `mainContent` for a dashboard that itself contains three further slots.
3232

3333
**Use case:** A dashboard page with a sidebar navigation, stats cards, an activity table, and a chart.
3434

@@ -80,7 +80,7 @@ Link: <https://example.com/views/dashboard.json>; rel="view-descriptor"
8080

8181
Multiple named views for the same API response. The client selects a view based on context (device class, user preference, layout mode).
8282

83-
**Use case:** A product page with a full detail view and a compact card view.
83+
**Use case:** A dashboard with a full detail view and a compact card view.
8484

8585
```json title="vdp-multi-view.json"
8686
{

‎docs/index.md‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ hide:
2323

2424
## What is VDP?
2525

26-
The **View Descriptor Protocol** defines a standard way for APIs to tell clients which templates to use for rendering data. A view descriptor is a JSON structure that identifies a root template by URL and declares how sub-templates compose into named **slots**, forming a recursive template tree.
26+
The **View Descriptor Protocol** defines a standard way for APIs to tell clients which templates to use for rendering data. A view descriptor is a JSON structure that names a root template by URL and declares which sub-templates fill its named **slots**. Because each slot is itself described by a view descriptor, descriptors form a recursive template tree.
2727

2828
VDP works with **any rendering framework** — HTML/Qute, SwiftUI, Jetpack Compose, React, or anything else that supports named insertion points.
2929

@@ -57,15 +57,15 @@ Embed view descriptors inline (`_view` / `_views` in HAL+JSON) or reference them
5757

5858
### Cacheable Descriptors
5959

60-
View descriptors are standalone resources with their own URLs, independently cacheable from the data they describe.
60+
View descriptors are standalone resources with their own URLs, cacheable independently of the data they describe.
6161

6262
</div>
6363

6464
<div class="vdp-feature" markdown>
6565

6666
### Cross-Platform
6767

68-
One API response, multiple views. Serve different template trees for desktop, mobile, compact, and full layouts from the same data endpoint.
68+
One API response, multiple views. Serve different template trees — desktop, mobile, compact — from the same data endpoint.
6969

7070
</div>
7171

‎docs/specification.md‎

Lines changed: 27 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@
55

66
## Abstract
77

8-
The View Descriptor Protocol (VDP) defines a standard mechanism for associating API data responses with the templates that should render them. A **view descriptor** is a JSON structure that identifies a root template by URL and declares how sub-templates compose into named slots, forming a recursive template tree. View descriptors can be transported via HTTP headers (for constrained formats like OData4) or inline in the response body (for flexible formats like HAL+JSON). The protocol is framework-agnostic — templates can be HTML/Qute, SwiftUI views, Compose layouts, or any other rendering format.
8+
The View Descriptor Protocol (VDP) defines a standard mechanism for associating API data responses with the templates that should render them. A **view descriptor** is a JSON structure that names a root template by URL and declares which sub-templates fill its named slots. Because each slot is itself described by a view descriptor, descriptors form a recursive template tree. View descriptors can be transported via HTTP headers (for constrained formats like OData4) or inline in the response body (for flexible formats like HAL+JSON). The protocol is framework-agnostic — templates can be HTML/Qute, SwiftUI views, Compose layouts, or any other rendering format.
99

1010
## 1. Problem Statement
1111

12-
REST APIs return structured data (JSON, XML) that carries no presentation information. The client must independently decide how to render this data — typically by hardcoding template choices into client logic. This creates tight coupling between API consumers and their rendering layer.
12+
REST APIs return structured data (JSON, XML) that carries no presentation information. Each client must decide on its own how to render that data — typically by hardcoding template choices into client code. As a result, every presentation change requires updating each client, and every client maintains its own copy of the same data-to-template mapping.
1313

1414
**VDP solves this by letting the server declare:**
1515

@@ -28,8 +28,8 @@ REST APIs return structured data (JSON, XML) that carries no presentation inform
2828
- **Template URL**: A URL identifying a template resource. The URL MUST resolve to a renderable template in the client's rendering framework.
2929
- **Slot**: A named insertion point in a template where a sub-template can be composed. Slot names correspond to the template's own insertion point identifiers (e.g., Qute's `{#insert slotName}`, HTML's `<slot name="slotName">`).
3030
- **View Descriptor Resource**: A standalone JSON document containing a view descriptor, addressable by its own URL, cacheable independently of the data it describes.
31-
- **Static Composition**: Template includes that are hardcoded within the template itself (e.g., a layout always including its `_head` partial). VDP does not manage these — they are the template's internal concern.
32-
- **Dynamic Composition**: Template slots whose content varies per API response. VDP manages these.
31+
- **Static Composition**: Composition written directly into a template's source — for example, a layout that always includes its `_head` partial. VDP does not describe static composition; it is internal to the template.
32+
- **Dynamic Composition**: Composition that changes per API response — a slot whose template is chosen by the server at request time. These are the slots a view descriptor declares.
3333

3434
## 3. View Descriptor Format
3535

@@ -99,7 +99,7 @@ Since each slot value is itself a view descriptor, composition nests to arbitrar
9999

100100
### 3.4 Multiple Views
101101

102-
A single API response may offer multiple views (e.g., a summary view and a detail view, or views for different device classes). Use a named object at the top level:
102+
A single API response may offer multiple views (e.g., a summary view and a detail view, or views for different device classes). Declare them as a named map under the `views` key:
103103

104104
```json
105105
{
@@ -187,8 +187,8 @@ The client fetches `https://example.com/views/dashboard.json` to get the view de
187187

188188
- Keeps the data payload completely clean
189189
- Works with **any** data format (JSON, XML, OData4, GraphQL, Protocol Buffers)
190-
- The view descriptor resource is independently cacheable
191-
- Uses existing web standards ([RFC 8288](https://www.rfc-editor.org/rfc/rfc8288) Link Relations)
190+
- Makes the view descriptor independently cacheable
191+
- Builds on existing web standards ([RFC 8288](https://www.rfc-editor.org/rfc/rfc8288) Link Relations)
192192

193193
**For simple cases** (single template, no composition), a shorthand header is also defined:
194194

@@ -325,19 +325,21 @@ Given an API response at `https://example.com/api/dashboard` with an inline view
325325
```json
326326
{
327327
"_view": {
328-
"template": "templates/layouts/sidebar",
328+
"template": "/templates/layouts/sidebar",
329329
"slots": {
330330
"mainContent": {
331-
"template": "templates/components/card"
331+
"template": "/templates/components/card"
332332
}
333333
}
334334
}
335335
}
336336
```
337337

338338
Both template URLs resolve against `https://example.com/api/dashboard`:
339-
- `templates/layouts/sidebar` → `https://example.com/templates/layouts/sidebar`
340-
- `templates/components/card` → `https://example.com/templates/components/card`
339+
- `/templates/layouts/sidebar` → `https://example.com/templates/layouts/sidebar`
340+
- `/templates/components/card` → `https://example.com/templates/components/card`
341+
342+
Note that resolution follows RFC 3986 exactly: a reference without a leading slash resolves relative to the base URL's path, so `templates/components/card` would yield `https://example.com/api/templates/components/card` instead.
341343

342344
Servers SHOULD use absolute URLs when view descriptors may be consumed by multiple clients with different base URL contexts.
343345

@@ -357,23 +359,23 @@ This keeps view descriptors small and avoids pushing selection logic into client
357359

358360
## 6. Template Requirements
359361

360-
VDP is agnostic to the template language. However, templates used with VDP MUST satisfy one requirement: **named insertion points (slots) that can be filled externally**.
362+
VDP is agnostic to the template language. However, a template used with VDP MUST meet one requirement: **it exposes named insertion points (slots) that can be filled from outside the template**.
361363

362364
### 6.1 Framework Slot Mappings
363365

364-
| Framework | Slot Mechanism | Example |
365-
|-----------|---------------|---------|
366-
| Qute | `{#insert slotName}{/insert}` | `{#insert mainContent}Default{/insert}` |
367-
| HTML `<template>` | `<slot name="slotName">` | `<slot name="mainContent"></slot>` |
368-
| HTMT | `ht-template="slotName"` | `<div ht-template="mainContent"></div>` |
369-
| Thymeleaf | `th:fragment` / `th:replace` | `<div th:replace="~{slotName}"></div>` |
370-
| JSX/React | `props.children` or named props | `{props.mainContent}` |
371-
| SwiftUI | `@ViewBuilder` parameters | `var mainContent: () -> Content` |
372-
| Jetpack Compose | `@Composable` slot parameters | `mainContent: @Composable () -> Unit` |
366+
| Framework | Slot Mechanism | Example |
367+
|-------------------|---------------------------------|-----------------------------------------|
368+
| Qute | `{#insert slotName}{/insert}` | `{#insert mainContent}Default{/insert}` |
369+
| HTML `<template>` | `<slot name="slotName">` | `<slot name="mainContent"></slot>` |
370+
| HTMT | `ht-template="slotName"` | `<div ht-template="mainContent"></div>` |
371+
| Thymeleaf | `th:fragment` / `th:replace` | `<div th:replace="~{slotName}"></div>` |
372+
| JSX/React | `props.children` or named props | `{props.mainContent}` |
373+
| SwiftUI | `@ViewBuilder` parameters | `var mainContent: () -> Content` |
374+
| Jetpack Compose | `@Composable` slot parameters | `mainContent: @Composable () -> Unit` |
373375

374376
### 6.2 Static vs Dynamic Slots
375377

376-
Not all insertion points in a template need to be managed by VDP. Templates commonly include static partials (like a shared `_head` or a footer) that are hardcoded. Only slots that vary per API response need to appear in the view descriptor.
378+
Not every insertion point in a template needs to appear in the view descriptor. Templates commonly include partials that never change — a shared `_head`, a footer — and those stay hardcoded in the template (static composition, Section 2). Only slots whose content varies per API response belong in the view descriptor (dynamic composition).
377379

378380
## 7. Examples
379381

@@ -514,7 +516,7 @@ This is the pattern used by **quarkus-pha**: Quarkus acts as the BFF, fetching d
514516
4. **Identify slot insertion points** in the template.
515517
5. **For each slot** declared in the view descriptor:
516518
a. Fetch the sub-template from its `template` URL.
517-
b. If the sub-template's view descriptor has `slots`, recurse (go to step 4).
519+
b. If the slot's view descriptor itself declares `slots`, repeat steps 3–5 for that descriptor.
518520
c. Insert the resolved sub-template into the slot.
519521
6. **Render** the composed template tree with the API response data.
520522

@@ -551,7 +553,7 @@ When a view descriptor is malformed (invalid JSON, missing required `template` f
551553

552554
### 9.4 Graceful Degradation
553555

554-
Error handling follows a principle of progressive failure:
556+
Error handling follows the principle that a failure stays as local as possible:
555557

556558
1. A single slot failure does not prevent the rest of the template tree from rendering.
557559
2. A root template failure prevents rendering entirely — the client falls back to raw data or a default template.
@@ -600,7 +602,7 @@ APIs SHOULD advertise VDP support so clients can detect it programmatically.
600602

601603
### 13.1 OPTIONS Response
602604

603-
An API endpoint supporting VDP MUST include the `VDP` token in the `Allow` or a custom header in its `OPTIONS` response:
605+
An API endpoint supporting VDP MUST advertise it in its `OPTIONS` response, using the `VDP-Support` and `VDP-Version` headers:
604606

605607
```http
606608
OPTIONS /api/dashboard HTTP/1.1

0 commit comments

Comments
 (0)