What is your developer-dependent CMS actually costing you? Calculate your costs and get a full report
What is your developer-dependent CMS actually costing you? Calculate your costs and get a full report
Architecture
One content source for a website, mobile app, email, kiosks and screens: pull, push or local copy per channel, keys per channel, caching, preview and failure modes.
Use this architecture when the same content has to reach more than a website: a mobile app, email campaigns, in-store kiosks, digital signage or another system. Editors work in one place, and each channel takes the content in the way that suits it.
Each channel uses one of three delivery patterns:
| Pattern | How it works | Best for |
|---|---|---|
| Pull | The channel calls the Content Fetch API or GraphQL API when it needs content, through a cache | Websites, apps with a backend, email sent from a server |
| Push | A webhook tells the channel's system that content changed, and that system pulls or rebuilds | Static builds, email templates, search indexes, other platforms |
| Local copy | Content Sync copies content into a store the channel owns, and keeps it current | Kiosks and screens that must work offline, very high read volume, data platforms |
See Publishing to Multiple Destinations for the idea, and Content Sync Explained for when to choose Sync over Fetch or GraphQL.
For channel-by-channel advice on reading content and modeling it to work in every channel, see Beyond the Website.
| Component | Owned by | Role |
|---|---|---|
| Agility instance | Agility (you configure it) | Shared content lists, plus a sitemap per channel that has pages or screens |
| Content Fetch and GraphQL APIs | Agility | Pull access for every channel |
| Webhooks | Agility | Push notifications on save, publish, unpublish and workflow events |
| Website | You | As in the marketing website architecture |
| App backend (recommended) | You | Reads from Agility, caches, and serves the app the shape it needs |
| Mobile app | You | Reads from your backend, or directly from the Fetch API with a fetch key |
| Email platform | Your vendor | Pulls content at send time, or receives it from your code when a webhook fires |
| Kiosk or screen player | You | Reads from a local store kept current by Content Sync |
Editors need to know which channel each item reaches. Pick one approach per content type:
These are described in detail in Publishing to Multiple Destinations.
| Channel | Pattern | How it reads | How it learns about changes |
|---|---|---|---|
| Website | Pull | Server-side Fetch or GraphQL, cached with tags | Webhook clears tags |
| Mobile app | Pull | From your backend, or the Fetch API with a fetch key | Your backend's cache is cleared by webhook; the app refreshes on launch or on a push notification you send |
| Pull or push | Your sending code fetches the content when it builds the message | A webhook on publish can update a template or trigger a send in your email platform | |
| Kiosk or signage | Local copy | Reads its own store, filled by @agility/content-sync | A webhook or a schedule runs an incremental sync from the stored token |
| Search or data platform | Push or local copy | Indexes from webhooks, or syncs | Webhooks for single items, Sync for bulk and recovery |
| Channel | Cache | Freshness |
|---|---|---|
| Website | Data cache and CDN | Seconds after publish, through webhooks |
| App backend | Data cache | Seconds after publish, through webhooks |
| Mobile app | On the device | Until the app next refreshes |
| None needed: content is read once at send time | Whatever was published at send time | |
| Kiosk | Its local store | Until the next sync |
Agility's Preview button opens the preview deployment registered for a sitemap. That suits the website and any channel you can render in a browser. For the others:
| What goes wrong | Effect | Design for it |
|---|---|---|
| A field is removed or renamed in a model | Older app versions still in use break | Change models additively. Add fields, stop using old ones, remove them only when no shipped version reads them |
| An app ships with a preview key | Unpublished content is readable by anyone who extracts it | Fetch keys only in clients, preview through your backend |
| A kiosk loses its network | Nothing, if it reads from its local store | Content Sync to a local store. Sync again when the network returns |
| A webhook is missed | One channel stays on old content | Turn on webhook retries, and run a scheduled sync or rebuild as a safety net |
| Many channels hit the API at the same moment | 429 responses | Put channels behind caches or a backend, and sync rather than poll. See Handle Rate Limits and Outages Gracefully |
| An email is built from unpublished content | Drafts reach customers | Build emails with the fetch key and the fetch API type only |