AEM-Architecture

AEM Architecture Fundamentals & Core Components

A Technical Deep-Dive for Developers & Architects

Adobe Experience Manager is one of the most powerful — and most misunderstood — platforms in
enterprise web development. This article gives you that map. By the time you reach the end, you’ll
be able to trace any request from a browser URL all the way through the stack to a rendered page,
and you’ll know exactly where things can go wrong at each layer.

Stack Layers Key Frameworks Min Read Time Skill Level
6 core layers OSGi, JCR, Sling, HTL ~20 minutes Intermediate–Expert

1. The AEM Technology Stack

AEM doesn’t run on magic. It runs on a carefully layered set of open standards. Understanding those layers is the difference between being reactive (“something is broken”) and proactive (“I know exactly what to check”).
The full stack, from bottom to top:
Layer Technology What It Does
6 (Top) AEM Applications Sites, Assets, Forms — the products you work with
5 Adobe Granite AEM's custom layer: workflows, tagging, replication, security
4 Apache Sling Maps URLs to content; drives the request pipeline
3 JCR / Apache Oak Content repository — everything is a node
2 OSGi (Apache Felix) Modular runtime — bundles, services, dynamic wiring
1 (Base) JVM (Java) The foundation — all of the above runs on the JVM

2. OSGi — The Modular Heart

OSGi is beautiful, until it isn’t. When it works, you get a hot-reloadable, loosely-coupled runtime where services wire themselves together at startup. When it breaks, you get a cascade of bundle failures that look completely unrelated to your change.
What Is OSGi?
OSGi (Open Services Gateway initiative) is a Java-based component system. In AEM, it’s implemented by Apache Felix. The entire AEM application — including your custom code — runs as a set of OSGi bundles inside this container.
The Four Building Blocks
Bundles
A bundle is a standard JAR file with extra metadata in its MANIFEST.MF. That metadata tells OSGi what packages the bundle exports (makes available) and what packages it imports (depends on).
Services
A service is a Java interface implemented by one or more bundles and registered in a service registry. Other bundles discover services through the registry — they never depend on a specific implementation class.
Components
A component is a Java class annotated with @Component (from the OSGi DS annotations). When the bundle activates, OSGi DS scans these annotations and instantiates/wires components automatically.
OSGi components
Configurations
Every component can have a corresponding factory configuration. Configurations are stored in the JCR repository (under /apps/config) or as .cfg.json/.config files in your project. They are environment-aware through run modes.

Production Gotcha — The Silent Run-Mode Miss

Your OSGi configuration for prod is under /apps/config.prod but your run modes are set to author,publish — not prod. Everything deploys without an error. Your production config is silently ignored. Always validate run-mode-specific configs with the Felix console after deployment.
OSGi Bundle Lifecycle

Production Gotcha — The OSGi Cascade Failure

You update Bundle A, which exports a package that Bundle B imports. Bundle B suddenly goes from Active to Installed state (its import is no longer satisfied during the update window). Bundle C depends on a service from Bundle B — it also goes down. One bundle update causes three services to disappear simultaneously. The Felix console’s Bundles view is your first diagnostic tool.

3. JCR & Apache Oak — Everything Is a Node

The Java Content Repository (JCR) is the storage backbone of AEM. Unlike a relational database with rows and columns, JCR stores everything — content, configuration, templates, component code, user data — as a hierarchy of nodes and properties.
The Repository Structure
The JCR tree has several standard root paths you’ll work with every day:
Path Purpose Example
/content All authored content — pages, assets, experience fragments /content/we-retail/en/home
/apps Custom application code — components, templates, OSGi configs /apps/my-project/components/page
/libs AEM product code — do not modify directly /libs/granite/ui/components/coral/foundation
/conf Editable template definitions and context-aware configs /conf/my-project/settings/wcm/templates
/var Variable data — workflow instances, audit logs, replication queues /var/workflow/instances
/home Users, groups, and their profile data /home/users/admin
Node Types
Every node has a primary type that defines what properties and child nodes it can have. Common node types in AEM include:
Apache Oak — The JCR Implementation
Apache Oak is the JCR 2.0 implementation powering AEM 6.x and AEM as a Cloud Service. Oak supports two storage backends:
Sling Resource Resolution and the JCR
Sling maps incoming URLs to JCR nodes. When a request arrives for /content/my-site/home.html, Sling walks the JCR tree to find a node at /content/my-site/home, then looks for the component referenced by that node’s sling:resourceType property to render it.

4. Apache Sling — The Request Pipeline

Apache Sling is the web framework that bridges the JCR and your component code. Its core insight is deceptively simple: every URL maps to a resource in the JCR, and every resource has a type that determines how it’s rendered.
URL Anatomy in Sling
A Sling URL follows a specific pattern that experienced AEM developers read like code:
Path Selectors Extension Suffix
/content/we-retail/home model.json .html /some/extra/path
The selectors (.model.json in the example above) are what drive multi-format rendering. The same resource at /content/page can render as HTML with no selector, as JSON with .model, and as an image thumbnail with .thumb.80.80 — all served by different scripts without any routing configuration.

Security Gotcha — Selector Injection

Sling’s open selector model is powerful but dangerous. An attacker requesting /content/page.infinity.json can dump an entire JCR subtree as JSON. Always configure your Sling URL mappings and Dispatcher rules to whitelist only the selectors your application exposes. The /system/console/slinglog viewer will show you every selector hit in real time.
The Sling Request Pipeline
When a request arrives, Sling processes it through a fixed pipeline:
Sling Models
Sling Models are Plain Old Java Objects (POJOs) annotated to automatically inject resource properties and OSGi services. They’re the recommended way to write business logic in AEM.
Sling Models
Sling Models

5. HTL (HTML Template Language)

HTL replaced JSP as the recommended server-side templating language for AEM. If you’re still writing Java directly in your template files, you’re doing it wrong — and you’re creating a security risk.
Why HTL Over JSP?
HTL Syntax Essentials
data-sly-use — Load a Sling Model
data-sly-use — Load a Sling Model
data-sly-list — Iterate a Collection
data-sly-list — Iterate a Collection
data-sly-test — Conditional Rendering
data-sly-test — Conditional Rendering
data-sly-include / data-sly-resource — Composition
data-sly-include / data-sly-resource — Composition

Best Practice — HTL Context Options

Always be explicit about output context. ${variable @ context=’html’} for HTML content, ${variable @ context=’uri’} for URLs, ${variable @ context=’attribute’} for HTML attributes. The default ‘text’ context strips all HTML — use ‘unsafe’ sparingly and only when you trust the source.

6. Client-Side Libraries (ClientLibs)

ClientLibs are AEM’s asset management system for CSS and JavaScript. They aggregate, minify, and version-stamp your front-end assets for performance and cache control.
ClientLib Structure
A ClientLib is a JCR node of type cq:ClientLibraryFolder located typically under /apps/my- project/clientlibs/. Key properties include:
Property Type Purpose
categories String[] Logical name(s) used to include this library in pages
dependencies String[] Other ClientLib categories that must load first
embed String[] Other ClientLib categories to inline into this one
js.txt / css.txt Text file Ordered list of JS/CSS files to aggregate
To include a ClientLib in a page template:
To include a ClientLib in a page template:

7. Run Modes — Environment Awareness

Run modes are how AEM differentiates between environments (author vs. publish) and deployment contexts (dev, staging, prod). They drive OSGi configurations, Sling content loading, and even which bundles are active.
Standard Run Modes
Run modes are set at startup via the sling.run.modes system property or in the quickstart JAR filename. You can have multiple active run modes simultaneously — e.g., author,prod.
Run-Mode-Specific Configurations
OSGi configuration files are placed in path-scoped folders under /apps/[project]/config:

Real-World Issue — Missing Publish Config

A common mistake is writing the correct OSGi configuration for the author instance but forgetting to create the publish-specific equivalent. Cache TTLs, replication agent settings, and authentication handlers often have different requirements per tier. Always peer-review config changes against both /config.author and /config.publish.

8. Replication — Author to Publish

When a content author clicks “Activate” in the AEM Sites console, AEM doesn’t just flip a flag. It triggers a replication workflow that serializes the JCR content from the author instance and transmits it to one or more publish agents.
The Replication Workflow
Replication Agents
Replication agents are configured under /etc/replication/agents.author/ and /etc/replication/agents.publish/. The most important ones are:

9. Dispatcher — The Performance & Security Layer

The Dispatcher is the last line of defense and the first line of performance. It sits in front of your publish tier, caches HTML and assets, load balances requests, and filters malicious traffic before it ever reaches AEM.
What the Dispatcher Does
Cache Invalidation
Dispatcher cache invalidation works through a stat file mechanism. When the Dispatcher Flush Agent triggers, the Dispatcher creates a .stat file in the cache directory. Any cached file older than the .stat file is considered stale and will be fetched fresh from AEM on the next request.

Production Gotcha — Cache Stampede

When a high-traffic page cache expires (or is intentionally invalidated after a publish), dozens of simultaneous requests can hit AEM before the first request has finished rendering and warming the cache. This “stampede” can bring down an AEM publish instance. Solutions include request coalescing in the Dispatcher, grace periods, and staggered activation schedules.
Key Dispatcher Configuration Files
File Purpose
dispatcher.any Main configuration: farms, filters, cache rules, load balancing
/cache/rules Which URLs to cache and for how long
/filter Whitelist/blacklist URL patterns; blocks /crx, /system, /apps, etc.
/renders Publish instance hostnames and ports to route requests to
/vanityUrls Resolves AEM vanity URL redirects before forwarding to publish

10. Tracing a Request End-to-End

This is where everything comes together. Let’s follow a real HTTP GET request from a browser all the way to a rendered AEM page.
Step Component What Happens
1 Browser Sends GET /content/we-retail/us/en/home.html
2 Load Balancer / CDN Routes to the Dispatcher (Apache + mod_dispatcher)
3 Dispatcher — Filter Checks /filter rules. If denied → 403. If allowed → continue.
4 Dispatcher — Cache Cache HIT → Serve file from disk, done. Cache MISS → Forward to publish.
5 AEM Publish — Sling URL decomposed. Path=/content/.../home, extension=html
6 Sling — Resource JCR node at path is resolved. sling:resourceType retrieved.
7 Sling — Script Finds /apps/my-project/components/page/page.html (HTL script)
8 HTL Engine Evaluates HTL; adapts resource to Sling Model; merges data into template
9 Sling Model @ValueMapValue and @OSGiService injections resolved; business logic runs
10 Response HTML returned → Dispatcher caches it → Browser renders page
Every step in this chain has a corresponding diagnostic tool. OSGi problems → Felix console. JCR problems → CRXDE Lite. Sling resolution problems → Sling Resource Resolver console. Dispatcher problems → mod_dispatcher logs. Knowing the chain means knowing exactly where to look.

11. Quick Reference — Diagnostic Tools

Problem Type Diagnostic Tool URL
OSGi bundle state Felix Web Console — Bundles /system/console/bundles
OSGi service wiring Felix Web Console — Components /system/console/components
OSGi configurations Felix Web Console — Configuration /system/console/configMgr
JCR content & structure CRXDE Lite /crx/de/index.jsp
Sling URL resolution Sling Resource Resolver /system/console/jcrresolver
ClientLib issues HTML Library Manager /libs/granite/ui/content/dumplibs.html
Replication status Replication Agents /etc/replication/agents.author.html
Request logs Log Support /system/console/slinglog

Conclusion

AEM is not a black box. It’s six well-documented open-source technologies stacked on top of each other, each with a specific responsibility, a standard API, and a console for introspection.
The developers who excel in AEM are not the ones who memorize the most APIs. They’re the ones who understand the shape of the system — who know that a missing OSGi configuration in a run- mode folder is why prod behaves differently than dev, that a Dispatcher filter rule is why a perfectly valid URL returns 403, and that a Sling resourceType miss is why a component renders nothing. You now have that map. The next step is to open the Felix console on a real AEM instance and start exploring. The architecture is all there, visible and interactive, waiting for you to poke at it.
topics-AEM

Recent Posts

AEM-Architecture
adobe-commerce-optimizor
CMS-Platforms
Connect with us