Edge Delivery Services · 13 of 29
Content storage in AEM and EDS. Covers JCR, da.live, the EDS content bus, the preview and publish pipeline, asset storage, and the path from authoring to browser render.
This chapter covers:
The question "where does content go?" sounds simple. The answer reveals the entire architectural philosophy of each system.
The Java Content Repository (JCR) is a hierarchical content storage API (JSR-283). AEM uses Apache Jackrabbit Oak as its JCR implementation.
The JCR stores everything in a tree of nodes. Every piece of content — pages, components, configurations, user data, workflow instances, audit logs — is a node or property in this tree.
/content/
├── mysite/
│ ├── en/
│ │ ├── home/
│ │ │ ├── jcr:content/ ← Page properties
│ │ │ │ ├── jcr:title = "Home Page"
│ │ │ │ ├── cq:template = "/conf/mysite/templates/home"
│ │ │ │ └── root/
│ │ │ │ ├── hero/
│ │ │ │ │ ├── jcr:primaryType = "nt:unstructured"
│ │ │ │ │ ├── size = "tall"
│ │ │ │ │ └── title = "Welcome"
│ │ │ │ └── cards/
│ │ │ │ └── ...
/apps/
├── mysite/ ← Application code
├── components/
└── templates/
/conf/
├── mysite/ ← Configuration
│ └── settings/
│ └── wcm/
│ └── templates/
/var/
├── workflow/ ← Workflow instances
└── audit/ ← Audit trail
/dam/
└── content/dam/ ← Digital assets
Persistence: Content is stored on disk in a segmented node store (Oak's SegmentNodeStore). The actual storage is in binary files (tar/segment format), not a traditional relational database.
Hierarchy: Every piece of content has a path. Content is addressed by path: /content/mysite/en/home/jcr:content.
Node Types: Every node has a type (nt:unstructured, cq:Page, dam:Asset, etc.). Types define which properties are allowed and which child nodes are valid.
Namespaces: Property names use namespaces (jcr:title, cq:template, sling:resourceType). Each namespace (jcr:, cq:, sling:) has a specific meaning in the AEM framework.
Binary Storage: Binary data (images, PDFs, videos) is stored in the DataStore — a blob storage layer separate from the node store, but referenced by nodes.
Versioning: The JCR has built-in content versioning. Every time a page is activated (published), a version is created. You can roll back to any previous version.
Access Control: ACLs (Access Control Lists) are stored as nodes (rep:policy children) directly in the content tree. Permissions are enforced at the repository level.
/content/mysite/en/home.html./content/mysite/en/home, extension .html./content/mysite/en/home/jcr:content and reads sling:resourceType = "mysite/components/page"./apps/mysite/components/page/page.html, instantiates the Sling Model (PageModel.java), which reads JCR properties via ValueMap, and HTL outputs HTML using the model data.The entire rendering pipeline runs on the server. No JCR content ever goes to the browser raw — the server transforms it into HTML.
EDS has two content storage backends, depending on which authoring approach you use.
When authors use Document Authoring, content is stored in da.live — Adobe's cloud content storage service built specifically for EDS.
Internally, da.live stores content as:
Content path: Each page is stored at a path that mirrors the URL structure:
da.live stores:
/mysite/en/home ← the home page document
/mysite/en/about ← the about page document
/mysite/en/news/article-1 ← a news article
Important: da.live content is NOT in JCR. It is not in AEM's node store. It is a completely separate storage system.
When authors use Universal Editor (xwalk), content IS stored in AEM JCR — the same storage as traditional AEM.
Pages created in UE are stored at:
/content/{site}/{lang}/{path}
The bin/franklin.delivery servlet on the AEM author instance reads this JCR content and converts it to EDS-compatible HTML when the Edge Network requests it.
This is why xwalk projects have a fstab.yaml pointing to the AEM author instance — EDS needs to fetch content from AEM's JCR via the delivery servlet.
.aem.page) now serves the new content..aem.live) now serves the new content.The Content Bus is EDS's internal content cache layer. It sits between the authoring system (da.live or AEM) and the edge delivery nodes.
The Content Bus:
.aem.page) and Live (.aem.live)Why content only updates on Preview click, not on save: The Content Bus is a production cache. If it updated on every save, partially completed edits would immediately appear on the preview site. The explicit Preview action is the author's declaration that "this content is ready to be seen."
POST /api/v1/web/helix-services/content-proxy/.../preview/{path}).https://author-pXXX.adobeaemcloud.com/bin/franklin.delivery/{owner}/{repo}/main/{path}.html.bin/franklin.delivery reads the AEM JCR content and converts the JCR component structure to EDS block HTML table format.The bin/franklin.delivery servlet is a critical bridge between AEM JCR and EDS. It:
/content/mysite/...)<!-- Component in JCR: hero-banner with size="tall", title="Welcome" -->
<!-- Converted to: -->
<table>
<tr><td>hero-banner</td></tr>
<tr><td>size</td><td>tall</td></tr>
<tr><td>title</td><td>Welcome</td></tr>
</table>
This conversion is what allows UE/xwalk content (stored in JCR) to be delivered by the EDS edge network (which expects HTML block tables).
Assets are stored in the DAM (Digital Asset Management):
/content/dam/...Assets are stored in:
For DA authoring: da.live asset storage. When an author inserts an image in the DA editor, it is uploaded to da.live and referenced by URL.
For xwalk authoring: AEM DAM. Images are stored in DAM at /content/dam/... exactly as in traditional AEM.
How EDS delivers images: EDS uses the Media Bus — a smart image delivery pipeline that:
<picture> elements with multiple <source> elements for different breakpoints<!-- EDS-generated responsive image markup -->
<picture>
<source type="image/webp" srcset="/media/hero.jpg?width=2000&format=webply&optimize=medium 2000w,
/media/hero.jpg?width=750&format=webply&optimize=medium 750w" />
<source srcset="/media/hero.jpg?width=2000&optimize=medium 2000w,
/media/hero.jpg?width=750&optimize=medium 750w" />
<img src="/media/hero.jpg?width=750&format=webply&optimize=medium"
width="750" height="500"
loading="eager" alt="Hero image" />
</picture>
This is generated automatically by EDS's image processing pipeline. You do not write this HTML. It is a significant advantage — automatic responsive images with WebP format at no developer effort.
AEM JCR has built-in content versioning:
/jcr:system/jcr:versionStorage/EDS does not have native content versioning at the CMS level.
For DA authoring: da.live maintains an internal history, but the UI for browsing/restoring versions is limited compared to AEM's versioning.
For xwalk authoring: Since content is in AEM JCR, AEM's full versioning capabilities are available. This is one advantage of the xwalk path.
Git as version control for code: All code changes (block JS, CSS, JSON models) are versioned in Git. You can roll back code changes with git revert.
Traditional AEM uses replication agents to push content from Author to Publish:
There is no replication. The Preview → Publish workflow replaces replication:
| AEM Concept | EDS Equivalent |
|---|---|
| Author instance | AEM author (xwalk) or da.live (DA) |
| Publish instance | EDS Content Bus (Live tier) |
| Dispatcher cache | EDS CDN edge cache |
| Activate (replicate) | Sidekick Publish action |
| Replication agent flush | Automatic CDN invalidation on Publish |
| Staged publishing | Preview tier (.aem.page) |
The key difference: AEM's replication is author-pushed (author triggers replication to publish). EDS is edge-pull (the edge network fetches from the authoring source when Preview is clicked).
+------------------+
| Author Instance|
| (AEM + JCR) |
+--------+---------+
|
Replication
|
+-----------+------------+
| Publish Instances |
| (AEM + JCR replica) |
+------+-------+----------+
| |
+------+ +------+
| |
+--------+--------+ +--------+--------+
| Dispatcher | | Dispatcher |
| (Cache + CDN) | | (Cache + CDN) |
+-----------------+ +-----------------+
|
Browser
+------------------+ +------------------+
| AEM Author | | da.live |
| (xwalk content | | (DA content |
| in JCR) | | storage) |
+--------+---------+ +--------+---------+
| |
bin/franklin.delivery da.live API
| |
+--------+-----------------------------+--------+
| EDS Content Bus |
| (Preview tier .aem.page / Live .aem.live) |
+---------------------------+--------------------+
|
+-----------+-----------+
| EDS CDN Edge Nodes |
| (Global, auto-scaling) |
+-----------+-----------+
|
Browser
(loads block JS/CSS from GitHub)
| In AEM (Sling Model) | In EDS (Block JS) |
|---|---|
resource.adaptTo(ValueMap.class).get("title", String.class) | props.title?.textContent.trim() |
resourceResolver.getResource("/content/data") | await fetch('/data/query-index.json') |
queryBuilder.createQuery(queryMap, session) | Client-side array filter on JSON index |
session.getNode("/content/dam/images") | Not available |
OSGi @Reference to service | import utility function |
| JCR observation for real-time updates | Not available |
EDS delivery is stateless. There is no session, no request context, no user authentication at the delivery layer.
If your application requires:
EDS delivers the same HTML to every user. Personalization happens client-side, after delivery.
AEM Content Fragments provide structured content modeling:
Content Fragments are AEM's answer to headless CMS. They are powerful but require the full AEM stack.
EDS does not have Content Fragments. Structured data is handled by:
JSON Sheets (Spreadsheet-based data): A SharePoint or Google Sheets spreadsheet is available as a JSON endpoint. This works for tabular data (pricing tables, product specifications, office locations).
Query API: Page metadata as structured data. If you store data in page metadata, the query API provides it as JSON.
External Headless CMS: For truly structured content, EDS blocks can fetch() from any external API — AEM Content Fragments API, Contentful, Hygraph, etc.
Authored block tables: Complex structured data can be authored as a block with many rows, each row representing one item. The block JS reads the table and renders the data.
da.live cloud storage — NOT in JCR, NOT in a traditional databasebin/franklin.deliveryThe next chapter, EDS Limitations and Alternatives, turns the storage model into architecture decisions: where EDS is the wrong tool, where to stay with AEM, and the hybrid patterns that combine both.
Get an email when I publish the next chapter. No spam — just new technical deep-dives.
Comments
Share feedback or questions about this blog post.
No comments yet. Be the first to share your thoughts.