Ways to Use EDS — Architecture Approaches and the Cost Justification
The six architectural approaches for using AEM Edge Delivery Services — pure DA, Universal Editor on AEM, AEM as a headless JSON backend, Content Fragments with GraphQL, external headless, and the hybrid same-domain pattern — with the approach we chose on the POC and an honest answer to "why pay for AEM and EDS?"
The architecture question that keeps coming up
By this point in the series, we have already covered the basics of blocks, authoring, content flow, and the real limitations of EDS.
At that point, the next question is usually not technical. It is budget and architecture driven:
We already pay for AEM. Why would we add EDS? Isn't that spending money twice?
And the follow-up question is usually:
There seem to be several ways to wire EDS to AEM. Which one do we use, and why?
This chapter is the answer to both of those questions.
It is not a theory chapter. It is the chapter that takes all the earlier architectural decisions and puts them next to each other so the trade-offs are visible. That matters because EDS only makes sense when you choose the right content source and the right delivery pattern for the use case.
1. The Core Mental Model
Before the approaches, hold one idea in your head.
EDS separates content from delivery. A page is assembled in the browser from small pieces of content plus block JavaScript and CSS. That content can come from anywhere that returns HTML or JSON:
- a document store (DA /
da.live) - AEM's JCR (via Universal Editor / xwalk)
- an AEM JSON endpoint (Sling Model Exporter, Content Fragment GraphQL)
- an external headless CMS
- any HTTP API
So "using EDS" is not one architecture. It is a delivery tier that can be fed by several different content sources. The approaches below are simply which source feeds which part of the page.
2. The Six Approaches, Side by Side
| # | Approach | Where content lives | Who authors | AEM needed? |
|---|---|---|---|---|
| A | Pure EDS + DA | da.live document store | Business users in Google Docs / Word / DA | No |
| B | EDS + Universal Editor on AEM (xwalk) | AEM JCR | Authors in the Universal Editor | Yes |
| C | AEM as a headless JSON backend | AEM JCR, exposed as JSON | Authors in AEM (classic) | Yes |
| D | Content Fragments + GraphQL | AEM JCR (structured CF models) | Authors in the CF editor | Yes |
| E | External headless CMS + EDS | Contentful / Hygraph / etc. | Authors in that CMS | No |
| F | Hybrid same-domain | Mixed — EDS and AEM on one domain | Both, per section | Yes |
The rest of the chapter walks each one, then chooses.
3. Approach A — Pure EDS + DA
The default EDS model. Content is authored as documents in DA (da.live), Preview
promotes it to the Content Bus, and blocks render it. No AEM instance is involved.
Where it fits: editorial sites — marketing, campaigns, product docs, news.
What you get: the lowest cost, the fastest authoring onboarding (it is a document), and the best structural Core Web Vitals.
What you give up: JCR, workflow, MSM, Content Fragments, DAM at scale, per-node ACLs — everything covered in Chapter 13.
Who pays: EDS entitlement only. No AEM license required.
4. Approach B — EDS + Universal Editor on AEM (xwalk)
Content lives in AEM's JCR, authors edit it in the Universal Editor, and it is
delivered through the EDS pipeline (bin/franklin.delivery). This is the "keep
AEM, gain EDS delivery" path covered in Chapter 17.
Where it fits: you already run AEM, your team manages content in JCR, and you want EDS performance without moving content out of AEM.
What you get: EDS delivery speed plus the AEM capabilities that survive in xwalk — workflow, MSM, DAM picker, Content Fragment references, node ACLs.
What you give up: more setup, an AEM Cloud instance to run, and content that is now coupled to JCR rather than a portable document store.
Who pays: AEM Cloud entitlement and EDS. This is where the "paying twice" question gets loudest — section 10 answers it.
5. Approach C — AEM as a Headless JSON Backend
This is the approach the question is really about: keep AEM as the content backend, expose the content as JSON, and consume it in an EDS block.
AEM produces JSON in two common ways:
- Sling Model Exporter — annotate a Sling Model and AEM serves its state as
JSON at
<path>.model.json. - Content Services /
.model.jsonon a content page.
The Sling Model:
@Model(
adaptables = Resource.class,
resourceType = "myco/components/promo",
defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)
@Exporter(name = "jackson", extensions = "json")
public class PromoModel {
@ValueMapValue private String title;
@ValueMapValue private String description;
@ValueMapValue private String ctaLabel;
@ValueMapValue private String ctaHref;
public String getTitle() { return title; }
public String getDescription() { return description; }
public String getCtaLabel() { return ctaLabel; }
public String getCtaHref() { return ctaHref; }
}
AEM then serves, at /content/myco/promos/spring.model.json:
{
"title": "Spring Sale",
"description": "Up to 40% off selected lines.",
"ctaLabel": "Shop now",
"ctaHref": "/campaigns/spring"
}
The EDS block fetches and renders it:
export default async function decorate(block) {
const endpoint = block.querySelector('a')?.href
|| block.textContent.trim();
block.textContent = '';
block.classList.add('is-loading');
try {
const res = await fetch(endpoint, { headers: { accept: 'application/json' } });
if (!res.ok) throw new Error(`Promo request failed: ${res.status}`);
const promo = await res.json();
block.innerHTML = `
<h2>${promo.title}</h2>
<p>${promo.description}</p>
<a class="button" href="${promo.ctaHref}">${promo.ctaLabel}</a>`;
} catch (err) {
block.classList.add('has-error');
// eslint-disable-next-line no-console
console.error(err);
} finally {
block.classList.remove('is-loading');
}
}
The two things this approach must solve:
- CORS. The AEM origin must send
Access-Control-Allow-Originfor the EDS domain, or the JSON must be served from the same domain (see Approach F). - Caching. A
.model.jsonhit is an origin request to AEM Publish. Put it behind the CDN with a sensible TTL, or you lose the EDS performance benefit by round-tripping to AEM on every render.
Where it fits: the content model already lives in AEM, authors will not move, but you want EDS to render the experience.
What you give up: you are back to running and paying for AEM Publish, and each JSON call is an origin dependency with its own failure and latency profile.
6. Approach D — Content Fragments + GraphQL
A more structured version of Approach C. Instead of a component's Sling Model, the content is a Content Fragment built on a CF Model (typed fields, variations, versioning), and AEM exposes it through GraphQL.
You define a persisted query in AEM (so the browser calls a stable, cacheable URL rather than sending a raw query):
# persisted query: promoByPath
query($path: String!) {
promoByPath(_path: $path) {
item {
title
description
cta { label href }
}
}
}
The EDS block calls the persisted-query endpoint:
export default async function decorate(block) {
const path = block.textContent.trim();
block.textContent = '';
const url = `https://author-or-publish.adobeaemcloud.com`
+ `/graphql/execute.json/myco/promoByPath;path=${encodeURIComponent(path)}`;
const res = await fetch(url, { headers: { accept: 'application/json' } });
const { data } = await res.json();
const promo = data.promoByPath.item;
block.innerHTML = `
<h2>${promo.title}</h2>
<p>${promo.description}</p>
<a class="button" href="${promo.cta.href}">${promo.cta.label}</a>`;
}
Why persisted queries matter here: they are the difference between a cacheable GET (good for EDS) and an ad-hoc POST query (an origin hit every time, and a larger attack surface). Always use persisted queries from browser code.
Where it fits: genuinely structured, reusable content — product data, author bios, location records — that is referenced from many pages and needs a schema.
What you give up: the full AEM stack to author and serve Content Fragments, plus the same CORS/caching work as Approach C.
7. Approach E — External Headless CMS + EDS
Content lives in a headless CMS (Contentful, Hygraph, Sanity, etc.). The EDS block
fetch()es it exactly like Approach C or D — the mechanics are identical, only the
origin changes. No AEM at all.
Where it fits: you want structured content and headless authoring but do not have (or do not want) AEM. This is the "EDS without AEM, but still structured" option.
What you give up: the AEM ecosystem — and you now pay for a separate CMS instead.
8. Approach F — Hybrid Same-Domain
The pattern most large enterprises converge on. EDS and traditional AEM live on one domain, and the CDN routes by path:
www.company.com/and/campaigns/*→ EDS (marketing, fast, edge-cached)www.company.com/app/*→ traditional AEM (authenticated application)www.company.com/forms/*→ AEM Formswww.company.com/api/*→ AEM JSON endpoints (Approach C/D), same-origin so no CORS problem
This is powerful because it combines the approaches instead of choosing one. The marketing site gets EDS performance; the application keeps AEM; and because it is one domain, EDS blocks can call AEM JSON endpoints same-origin, which quietly removes the biggest headache of Approaches C and D.
Where it fits: enterprises with an existing AEM investment and a marketing site that needs to be fast.
Who pays: both — but see the justification below, because here the double spend is usually the correct decision.
9. What We Chose on the POC, and Why
On the ADC POC the target was an editorial/marketing experience with authorable blocks, plus one form that had to reach a secured enterprise API. We used a combination rather than a single approach:
- Approach B (EDS + Universal Editor on AEM) for authoring. The content model
and the authoring team already lived in AEM, and xwalk let us keep content in
JCR while delivering through EDS. This is why the whole
component-models.json/_page.jsonmachinery in earlier chapters exists. - A serverless proxy for the secured form call, not AEM — covered in
Chapter 28. The secret could not live in
browser code, and on a pure EDS surface there is no
/binservlet to hold it.
We deliberately did not reach for Approach C/D (AEM JSON backend) for the
general content, because the content was authored, not structured-and-shared, and
adding a .model.json origin dependency to every block would have traded away the
EDS performance we were there to prove.
The lesson: the approaches are not mutually exclusive. Pick per surface. Author content through the surface that owns it; call an API only where you genuinely need runtime data.
10. The Cost Justification: Why Pay for AEM and EDS?
Here is the blunt question answered directly.
If you already pay for AEM, adding EDS is justified when EDS buys you something AEM cannot easily give you on the same money:
-
Performance as a structural guarantee. AEM can hit good Core Web Vitals, but it takes sustained effort — caching, clientlib discipline, Dispatcher tuning. EDS starts at a near-perfect Lighthouse score by construction. If Core Web Vitals are a ranking and revenue lever for you, that structural advantage is the return on the extra spend.
-
Authoring velocity for marketing. DA authoring (Approach A) or EDS-delivered authoring lets a marketing team ship pages without a deployment pipeline. The saving is not the license — it is the reduced dependency on developer time for routine content.
-
Developer velocity.
git pushto deploy a block, no full AEM build/pipeline for front-end changes. Faster iteration on the customer-facing layer. -
You keep the AEM investment. Approaches B, C, D, and F all preserve AEM as the content system. You are not throwing AEM away — you are adding a faster delivery tier in front of the content you already manage. The spend is incremental, not duplicated, because the two systems do different jobs: AEM manages content, EDS delivers it.
The one-line version: you are not paying twice for the same thing. You are paying AEM to manage content and EDS to deliver it fast. The double spend is justified exactly when delivery performance and authoring/dev velocity are worth more than the incremental EDS cost — which, for a high-traffic marketing surface, they usually are.
11. When EDS Is a Waste — The Honest Inverse
Adding EDS is not justified, and you should stay on plain AEM, when:
- The experience is a transactional application, not editorial content. EDS's no-server model means every dynamic need becomes an external API call. If most of the site is dynamic, you are fighting the architecture.
- You need deep server-side personalization or complex per-request rendering. That is AEM's home turf; EDS pushes it all client-side.
- AEM Forms is central. There is no EDS-native equivalent (Chapter 13).
- You need per-component access control or strict workflow enforcement on a regulated content library.
- Your traffic is low and performance is not a business lever. Then the EDS performance win does not pay for the added architectural surface, and a single well-tuned AEM stack is simpler and cheaper.
If two or more of these are true for the whole site, EDS is the wrong spend. If they are true only for one section, use Approach F and let each section run on the technology that fits it.
12. A Decision Path
Walk this top to bottom and stop at the first match:
- Is the surface a dynamic application, forms-heavy, or personalization-heavy? → Stay on AEM. Do not add EDS for that surface.
- Is it editorial, and do you have no AEM (or want none)? → Approach A (pure EDS + DA), or Approach E if you need structured headless.
- Is it editorial, you have AEM, and authors will stay in AEM? → Approach B (Universal Editor / xwalk).
- Do EDS pages need structured, reusable data that already lives in AEM? → Approach C (Sling Model JSON) for component-shaped data, or Approach D (Content Fragments + GraphQL) for schema-modelled data.
- Do you have both a fast marketing site and a real AEM application? → Approach F (hybrid same-domain), combining the above per path.
Key Takeaways
- "Using EDS" is not one architecture — it is a delivery tier fed by one of several content sources.
- The six approaches: pure DA, Universal Editor on AEM, AEM-as-JSON-backend, Content Fragments + GraphQL, external headless, and hybrid same-domain.
- AEM-as-JSON works through the Sling Model Exporter or Content Fragment GraphQL; from the browser, always use cacheable endpoints (persisted queries) and solve CORS and caching or you lose the EDS performance benefit.
- The hybrid same-domain pattern is the enterprise sweet spot: EDS blocks call AEM JSON same-origin, so CORS disappears.
- The approaches are not exclusive — choose per surface. On the POC we used Universal Editor for content and a serverless proxy for the secured form.
- You are not paying for AEM and EDS twice for the same job: AEM manages content, EDS delivers it fast. The extra spend is justified by performance and velocity.
- EDS is a waste when the surface is transactional, forms-heavy, personalization- heavy, access-control-heavy, or low-traffic — then stay on AEM, or isolate those parts with the hybrid pattern.
Next Steps
With the architecture choice made, the rest of the series returns to building. The next chapter, CSS Architecture in EDS, covers how EDS structures styling without BEM, how block CSS is scoped, and the conventions that keep a growing block library maintainable.
Enjoyed this chapter?
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.