14 KiB
Published Resource Architecture
Platform-wide abstraction for anything that can be published publicly.
Normative decision: ADR-022
Experience ownership: ADR-016 · Roadmap: experience-roadmap.md
Contracts: published-resource-contracts.md
Status: Accepted · ARCHITECTURE COMPLETE (documentation freeze — no implementation in this patch).
Final freeze adds: Published Actions, Public Access Model, Universal Embed Architecture (additive, backward compatible).
1. Purpose
TorbatYar products publish many public surfaces (sites, pages, forms, menus, cards, events, …). Without a shared abstraction, each vertical and future product invents its own public identity, URL scheme, and cross-service coupling.
Published Resource is the official platform abstraction for every present and future public surface. All consumers (Payment, Communication, Short Link, QR, Analytics, CRM, verticals, future products) integrate via publish_id only — never via Experience (or other) internal tables.
2. Published Resource
A Published Resource represents anything that can be published publicly for a tenant.
| Field | Meaning |
|---|---|
publish_id |
Stable public identity (UUID). Cross-service foreign key substitute. |
tenant_id |
Owning tenant (ADR-003) |
resource_type |
Extensible type key (string registry — not a closed enum in architecture) |
resource_id |
Owning-service aggregate id (opaque to consumers) |
canonical_url |
Canonical public URL path or absolute URL |
publish_status |
Lifecycle: draft · published · unpublished · archived (extensible) |
visibility |
e.g. public · unlisted · tenant_only (extensible) |
owner_service |
Service that owns the aggregate (experience, hospitality, …) |
Resource type registry (open)
Architecture must not hardcode a finite type list. Types are registered as string keys. Illustrative examples (non-exhaustive):
Example resource_type |
Typical owner |
|---|---|
site |
experience |
page |
experience |
landing_page |
experience |
form |
experience |
survey |
experience |
appointment |
experience |
restaurant_menu |
hospitality (surface may publish via Experience) |
product_catalog |
marketplace / ecommerce |
campaign |
experience / marketing consumers |
digital_business_card |
experience / future Torbat Card |
event |
experience / future Torbat Event |
booking_page |
experience / future Torbat Booking |
| future.* | any registered service |
Unlimited future types are allowed without ADR revision when they follow this contract.
3. Publish Target Registry
The Publish Target Registry is the logical catalog of all Published Resources.
Rules:
- Every publicly reachable surface that other services may attach to MUST have a registry entry with a
publish_id. - Registry entries are tenant-scoped.
- Consumers resolve and attach using
publish_id(+tenant_idfor isolation checks). - Registry ownership for Experience-authored surfaces is Experience (
experience_db). Other owner services may publish their own surfaces into the same logical contract (API/events), never by writing Experience tables. - No implementation in this documentation patch — schema, API, and migrations are deferred to future registered phases.
Logical record:
PublishTarget {
publish_id
tenant_id
resource_type
resource_id
canonical_url
publish_status
visibility
owner_service
# optional metadata (locale, title_ref, …) — additive only
}
4. Independent Resources
The following Experience resource kinds MUST NOT require a parent Site or Page to exist or publish:
| Resource | Standalone publish | Optional Site binding |
|---|---|---|
| Form | Required | Allowed |
| Survey | Required | Allowed |
| Appointment (appointment page / booking shell) | Required | Allowed |
Rules:
- Forms, Surveys, and Appointments are first-class publishable resources.
- They may optionally belong to a Site (and/or Page) for composition.
- They must support standalone publishing with their own
publish_idand canonical URL. - UI and API designs must not force “create Site → create Page → attach Form” as the only path.
Sites and Pages remain publishable resources; independence applies to Form / Survey / Appointment aggregates specifically.
5. Cross-Service Integration Contracts
All integrations are documentation contracts only until a registered implementation phase.
| Consumer | Attachment | Reference key |
|---|---|---|
| Payment (Torbat Pay) | Paid form / appointment / booking / registration / event | publish_id |
| Communication | SMS/email/OTP/notify after submit or booking | publish_id |
| Short Link | Resolve short codes to public surfaces | publish_id |
| QR Code | Encode / resolve posters, tables, cards, events | publish_id |
| Analytics | Pageviews, conversions, funnels | publish_id |
| CRM | Lead capture / contact refs from public surfaces | publish_id |
| Booking | Scheduling refs attached to public bookable surfaces | publish_id |
| Marketplace | Catalog embed / campaign surfaces | publish_id |
| Hospitality | Menu / venue public surfaces | publish_id |
| Delivery | Campaign / logistics announcement surfaces | publish_id |
| Loyalty | Offer / card / campaign surfaces | publish_id |
| Sports Center | Membership / event / booking surfaces | publish_id |
| Future services | Any public attachment | publish_id |
Forbidden: consumers must not query Experience (or other) internal tables, import models, or store foreign primary keys as substitutes for publish_id when the public surface is the integration target.
Detail: published-resource-contracts.md.
6. Standard Public URL Patterns (Reserved)
Routing is not implemented in this patch. The following path prefixes are reserved for public resolution:
| Prefix | Intended use (illustrative) |
|---|---|
/s/ |
Site / published site shell |
/p/ |
Page / landing |
/f/ |
Form |
/survey/ |
Survey |
/book/ |
Appointment / booking page |
/menu/ |
Restaurant / digital menu surface |
/card/ |
Digital business card |
/event/ |
Event surface |
Additional prefixes may be reserved later without breaking this architecture. Canonical URLs stored on the Publish Target Registry should align with these patterns when applicable.
7. Future Products
The following commercial products are architecture-reserved. They consume Published Resources; they never own Experience data:
| Product | Role |
|---|---|
| Torbat Link | Short / branded link layer over Published Resources |
| Torbat QR | QR issuance and resolution to publish_id |
| Torbat Card | Digital business card surfaces |
| Torbat Booking | Booking UX over appointment / bookable resources |
| Torbat Ticket | Ticketing over event / paid registration resources |
| Torbat Event | Event publishing and discovery |
| Torbat Learning | Learning / knowledge page surfaces |
These products integrate through API/Events + publish_id. Experience remains owner of page/site/theme/form aggregates it already owns (ADR-016).
8. Payment Integration
Payment (ADR-020, ADR-021) may attach to any Published Resource.
Examples: Paid Form · Paid Appointment · Paid Booking · Paid Registration · Paid Event.
Contract: Payment request / checkout session stores publish_id (and tenant_id). Payment must not own Experience aggregates or read experience_db.
9. Communication Integration
Communication (ADR-012) triggers may fire on Published Resource lifecycle or submission events.
Examples: SMS after submit · Email after booking · OTP · Notifications · Campaign messages.
Contract: templates and dispatches reference publish_id (plus channel/template keys owned by Communication). Providers remain Communication-owned.
10. Short Link Integration
Short Link ownership is reserved as a future/adjacent product capability (e.g. Torbat Link).
- Short codes resolve to Published Resources via
publish_id. - No direct dependency on Experience internals.
- Experience must not embed a competing short-link engine as source of truth.
11. QR Integration
QR codes resolve to Published Resources via publish_id.
Examples: Restaurant Table · Business Card · Poster · Event · Form · Appointment.
QR issuance may live in a future Torbat QR product or vertical adapters; payload always targets the Publish Target Registry.
12. Analytics
Analytics (Experience shells and future platform analytics) record publish_id for public funnel metrics.
- No ownership change: Experience analytics shells remain Experience-owned COUNT/local shells where already defined.
- Cross-service analytics consumers also key off
publish_id, not internal page/form table ids.
13. Ownership & Boundaries
| Concern | Owner |
|---|---|
| Publish Target Registry for Experience-authored surfaces | Experience (experience) |
| Page / site / theme / template / form / survey / appointment shells | Experience |
| Publishing, presentation, rendering metadata, embed metadata | Experience |
| Published Action presentation bindings | Experience (refs action_key) |
| Published Action business execution | Owning service of the action |
| Public Access Policy metadata | Experience |
| Authentication / SSO | Identity / Core |
| Payment capture / ledger | Payment |
| Membership gates | Loyalty |
| OTP / messaging | Communication |
| Menu/item catalog source of truth | Hospitality |
| Product catalog source of truth | Marketplace / Ecommerce |
| Media binaries | File Storage |
| Short Link / QR issuance engines | Reserved products |
| Public URL edge routing | Platform edge / frontend (reserved patterns) |
| Builder UI | Frontend (ADR-002) |
All cross-service attachments continue to reference publish_id only.
14. Published Actions
Every Published Resource may expose zero or more Actions. Actions are service-independent contracts registered in an open Action Registry.
| Field | Meaning |
|---|---|
action_key |
Open string key |
display_name |
Label |
owning_service |
Executes business logic |
permission |
Invocation permission |
capability |
Discovery key |
feature_toggle |
Optional enablement |
supported_resource_types |
Open type list |
Illustrative keys (not closed): View, Submit, Book, Reserve, Pay, Donate, Register, Download, Open Chat, Send Message, Share, Copy Link, Generate QR, Scan QR, Call, Navigate, Add To Calendar, Automation Trigger, Webhook Trigger, …
Future services register new actions without modifying Experience. Normative contract: published-action-registry.md.
15. Public Access Model
A Public Access Policy may compose open strategies, including: Public, Private, Authenticated, Tenant Members, Role Based, Password Protected, OTP Protected, Paid Access, Invitation Only, Signed URL, Temporary Link, Expiration, Rate Limited, Geo Restricted (reserved), and future strategies.
| Must not live in Experience | Owner |
|---|---|
| Authentication | Identity / Core |
| Payment unlock | Payment |
| Membership evaluation | Loyalty |
| OTP delivery | Communication |
Normative contract: public-access-contract.md.
16. Universal Embed Architecture
Published Resources may embed into external sites/apps. Reserved embed types (open): Iframe, JavaScript SDK, Widget, Popup, Inline, Modal, Floating Button, future types.
Embed contracts cover: responsive sizing, security/sandbox, origin validation, permission delegation, token passing, theming inheritance, analytics propagation (publish_id).
Normative contract: embed-contract.md.
17. Capability Discovery (additive, docs only)
Future /capabilities (or equivalent discovery) MAY advertise:
published_resources: truepublished_actions: true(registry available)public_access_policies: trueuniversal_embed: true- Open lists of known
action_key/embed_type/ access strategy keys
No API change in this patch — discovery additions are reserved for implementation phases.
18. Architecture Complete
With Published Resources, Publish Target Registry, Published Actions, Public Access Model, and Universal Embed Architecture documented and accepted under ADR-022, the Experience Platform architecture is ARCHITECTURE COMPLETE.
Remaining work is implementation (backend registry/APIs, frontend builders/public shells) in future registered phases — not architecture redesign.
19. Non-Goals (Architecture Patches)
- No backend schema, migrations, or APIs
- No frontend UI, embed SDK, or public routers
- No Short Link / QR / Card / Booking product implementation
- No changes to completed Experience phases 11.0–11.10 business code
- No hardcoded closed enums of resource types, actions, access strategies, or embed types