6.1 Maven Project Structure & AEM Project Archetype
Key Takeaways
- The AEM Project Archetype generates a standardized multi-module Maven reactor project separating concerns across OSGi Java bundles, immutable application code, mutable content, OSGi configurations, and frontend assets.
- The core module compiles into a standard OSGi bundle JAR containing backend business logic, Sling Models, OSGi Declarative Services, Servlets, and repoinit-invoking jobs.
- The ui.apps module packages immutable customer application code under /apps with package type application; custom projects must not place code under the product-owned /libs tree.
- The all module serves exclusively as a container aggregator using the filevault-package-maven-plugin to embed core, ui.apps, ui.config, and ui.content into a single deployable artifact for Cloud Manager.
- In AEM as a Cloud Service, OSGi configurations are decoupled from ui.apps into a dedicated ui.config module using .cfg.json syntax and targeted runmode folders.
6.1 Maven Project Structure & AEM Project Archetype
Quick Answer: The AEM Project Archetype generates a multi-module Maven reactor where the all module aggregates subpackages, core compiles the OSGi Java bundle, ui.apps packages immutable code under
/apps, ui.config stores OSGi configurations in.cfg.jsonformat, ui.content manages mutable authorable content (/content,/conf), ui.frontend compiles modern client-side assets via Webpack, and dispatcher packages Apache/Dispatcher web-tier configs. The reactor rootpom.xmlmanages dependencies using<scope>provided</scope>for the Cloud Service SDK (aem-sdk-api).
Enterprise implementations of Adobe Experience Manager (AEM) require robust build automation capable of orchestrating diverse technologies: Java OSGi bundles, Apache Jackrabbit FileVault JCR content packages, modern front-end build pipelines (TypeScript, Webpack, Sass), and Apache HTTP Server/Dispatcher configurations. To standardize project structures across the global ecosystem, Adobe maintains the AEM Project Archetype (com.adobe.aem:aem-project-archetype).
Adhering to the archetype conventions is not merely a stylistic recommendation; it is an architectural prerequisite for building, testing, and deploying solutions via Adobe Cloud Manager for both AEM as a Cloud Service (AEMaaCS) and AEM 6.5.
The Multi-Module Reactor Architecture
A modern AEM codebase uses a Maven multi-module reactor. Rather than building a single monolithic archive, Maven executes a top-down build managed by a root pom.xml with <packaging>pom</packaging>. The root POM defines compilation order, orchestrates plugin execution, and manages shared dependency versions.
my-project/
├── pom.xml <-- Root Reactor POM
├── core/ <-- OSGi Bundle (Java, Sling Models, Servlets)
│ └── pom.xml
├── ui.apps/ <-- Immutable Application Code (/apps)
│ └── pom.xml
├── ui.config/ <-- OSGi Configurations (/apps/.../osgiconfig)
│ └── pom.xml
├── ui.content/ <-- Mutable Content & Editable Templates (/content, /conf)
│ └── pom.xml
├── ui.frontend/ <-- Webpack / Front-End Build Pipeline
│ ├── package.json
│ └── pom.xml
├── all/ <-- Pure Container Aggregator Package
│ └── pom.xml
└── dispatcher/ <-- Apache HTTPD and Dispatcher Configurations
└── src/
Module Responsibilities & Target Artifact Matrix
| Module | Packaging Type | Artifact Output | Target Deployment Location | Primary Contents |
|---|---|---|---|---|
core | jar / bundle | OSGi Bundle JAR | Embedded into all (/apps/<app>/install) | Java source code, Sling Models, OSGi Declarative Services, Sling Servlets, Workflow steps, Event Listeners. |
ui.apps | content-package | Application Content Package | /apps (customer application code on the immutable mount) | HTL component scripts, Granite UI dialogs (cq:dialog), component clientlibs, rendering templates. |
ui.config | content-package | Application Content Package | /apps/<app>/osgiconfig | OSGi .cfg.json configurations organized by runmode folders (config, config.author, config.publish). |
ui.content | content-package | Content Package | /content, /conf, /content/dam (Mutable) | Editable template structures, Content policies, DAM asset folders, Initial site navigation structures. |
ui.frontend | pom | Clientlib Directory (Static CSS/JS) | Copied into ui.apps clientlibs | Webpack/Vite configs, TypeScript, SCSS, ESLint, Node.js dependencies (package.json). |
all | content-package | Container Content Package | Cloud Manager CI/CD Target | Embeds core, ui.apps, ui.config, and ui.content. No raw content of its own. |
dispatcher | pom | Deployment Archive (.tar.gz) | Dispatcher web tier (Apache HTTPD) | Virtual host files (*.vhost), Farm files (*.farm), Rewrite rules, Filter configurations. |
Deep Dive: Submodule Roles & Boundaries
1. core Module: OSGi Java Bundle
The core module contains all backend Java code. It compiles using the bnd-maven-plugin (or the legacy maven-bundle-plugin) to produce an OSGi R7 compliant bundle JAR containing an OSGi manifest (MANIFEST.MF) with generated Export-Package and Import-Package headers.
Key responsibilities include:
- Sling Models: Component backend adapters annotated with
@Modelutilizing@ValueMapValue,@ChildResource, and@Self. - OSGi Services & Components: Business logic annotated with OSGi Declarative Services annotations (
@Component,@Reference,@Activate,@Modified). - Sling Servlets: REST endpoints bound via
@Component(service = Servlet.class)usingsling.servlet.pathsorsling.servlet.resourceTypes. - Unit Tests: Mock-driven unit testing using JUnit 5, Mockito, and the
io.wcm.testing.aem-mock.junit5library.
Exam Trap: The
coremodule must never contain HTL scripts, dialog XML, or client-side assets. Furthermore, it should never directly manipulate repository content upon bundle activation using administrative sessions (loginAdministrative); all initial data setup must be delegated to Sling Repoinit.
2. ui.apps Module: Immutable Application Code
The ui.apps module packages the frontend component scripts, dialog definitions, and client library structures. Under AEM as a Cloud Service, customer code from ui.apps deploys to the immutable application area under /apps; /libs is product-owned and must not contain custom code.
Key contents include:
- Component definitions:
/apps/<app>/components/structure,/apps/<app>/components/content. - HTL rendering scripts:
component.html, template partials. - Granite UI component dialogs:
_cq_dialog/.content.xmldefining authoring touchpoints. - Client-side libraries:
/apps/<app>/clientlibscontaining CSS, JS, and font assets produced byui.frontend.
In ui.apps/pom.xml, the package type is explicitly declared as application via the filevault-package-maven-plugin:
<plugin>
<groupId>org.apache.jackrabbit</groupId>
<artifactId>filevault-package-maven-plugin</artifactId>
<configuration>
<packageType>application</packageType>
<repositoryStructurePackages>
<repositoryStructurePackage>
<groupId>com.mycompany.myproject</groupId>
<artifactId>myproject.ui.apps.structure</artifactId>
</repositoryStructurePackage>
</repositoryStructurePackages>
</configuration>
</plugin>
3. ui.config Module: Dedicated OSGi Configuration
In earlier archetype versions, OSGi configurations resided inside ui.apps. Modern AEM Project Archetypes isolate all OSGi configurations into a dedicated ui.config module.
Why this separation exists:
- Configuration Lifecycle: OSGi configurations undergo different governance and deployment checks than component templates and HTL scripts.
- Package Purity: Keeps
ui.appsfocused purely on component code, whileui.configmanages runmode-specific settings in/apps/<app>/osgiconfig. - Configuration Format: Enforces the modern Apache Sling OSGi configuration JSON format (
.cfg.json) instead of legacy.configor.xmlformats.
ui.config/src/main/content/jcr_root/apps/myproject/osgiconfig/
├── config/ <-- All environments
│ └── org.apache.sling.commons.log.LogManager.factory.config~myproject.cfg.json
├── config.author/ <-- Author runmode only
│ └── com.mycompany.core.services.AuthorService.cfg.json
├── config.publish/ <-- Publish runmode only
│ └── com.mycompany.core.services.PublishService.cfg.json
├── config.stage/ <-- Staging environment only
└── config.prod/ <-- Production environment only
4. ui.content Module: Mutable Runtime Content
The ui.content module packages nodes that live in the mutable Oak repository mount. This includes:
- Editable Templates & Policies: Stored under
/conf/<app>/settings/wcm/templatesand/conf/<app>/settings/wcm/policies. - DAM Asset Folders: Initial asset folder metadata under
/content/dam/<app>. - Site Structure: Baseline site nodes under
/content/<app>. - Experience Fragments: Template structures under
/content/experience-fragments/<app>.
The ui.content/pom.xml explicitly defines <packageType>content</packageType>. Filter rules in filter.xml must use safe import modes (mode="merge" or mode="update") to prevent overwriting author-created pages and policies in production environments.
5. ui.frontend Module: Modern Client-Side Tooling
Rather than forcing front-end developers to write raw ES5 JavaScript inside CRXDE Lite, the ui.frontend module provides an isolated, modern front-end environment with Webpack, Babel, TypeScript, PostCSS, and Sass.
Compilation lifecycle:
- The Maven build delegates to
frontend-maven-plugin. - The plugin downloads node.js and npm into the local working directory (no global installation required).
- It executes
npm installandnpm run build(ornpm run prod). - Webpack bundles the compiled minified CSS, JS, and source maps directly into the
ui.appssource directory atui.apps/src/main/content/jcr_root/apps/<app>/clientlibs/clientlib-site. - When Maven subsequently builds
ui.apps, the generated clientlib is bundled into the application package.
6. all Module: The Container Aggregator
The all module produces a single container package (<packageType>container</packageType>). It does not contain any JCR content, HTL, or Java files. Instead, its sole purpose is to embed all other compiled artifacts into a single installable package for Cloud Manager.
In all/pom.xml, the filevault-package-maven-plugin embeds the subpackages and OSGi bundle:
<plugin>
<groupId>org.apache.jackrabbit</groupId>
<artifactId>filevault-package-maven-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<packageType>container</packageType>
<embeddeds>
<!-- Embed Java OSGi Bundle -->
<embedded>
<groupId>com.mycompany.myproject</groupId>
<artifactId>myproject.core</artifactId>
<target>/apps/myproject-packages/application/install</target>
</embedded>
<!-- Embed ui.apps Package -->
<embedded>
<groupId>com.mycompany.myproject</groupId>
<artifactId>myproject.ui.apps</artifactId>
<type>zip</type>
<target>/apps/myproject-packages/application/install</target>
</embedded>
<!-- Embed ui.config Package -->
<embedded>
<groupId>com.mycompany.myproject</groupId>
<artifactId>myproject.ui.config</artifactId>
<type>zip</type>
<target>/apps/myproject-packages/application/install</target>
</embedded>
<!-- Embed ui.content Package -->
<embedded>
<groupId>com.mycompany.myproject</groupId>
<artifactId>myproject.ui.content</artifactId>
<type>zip</type>
<target>/apps/myproject-packages/content/install</target>
</embedded>
</embeddeds>
</configuration>
</plugin>
Exam Rule: Notice the target install paths in the container package:
- Application packages and OSGi bundles (
ui.apps,ui.config,core) target/apps/<app>-packages/application/install.- Content packages (
ui.content) target/apps/<app>-packages/content/install. Cloud Manager uses these distinct install paths to sequence installation between immutable and mutable storage mounts.
7. dispatcher Module: Web Tier Configurations
The dispatcher module houses Apache HTTP Server configurations (httpd.conf, conf.d/*.conf) and Dispatcher configurations (conf.dispatcher.d/*.farm, rules.any, filters.any).
In AEM as a Cloud Service, Dispatcher files are validated during the build using the Dispatcher Validator (bin/validate.sh) provided in the AEM SDK Dispatcher Tools. Cloud Manager packages the validated configuration directory into a .tar.gz archive and deploys it directly to the managed Apache/Dispatcher Docker containers.
Root Reactor POM: Dependency & Plugin Management
The root pom.xml orchestrates the entire multi-module reactor. It defines module order, sets common Java compiler versions (Java 11 or 17), and controls dependency resolution through <dependencyManagement> and <pluginManagement>.
Dependency Management & aem-sdk-api
In modern AEMaaCS projects, custom bundles compile against the unified aem-sdk-api artifact. This replaces the legacy monolithic uber-jar from AEM 6.x.
<dependencyManagement>
<dependencies>
<!-- AEM as a Cloud Service SDK API -->
<dependency>
<groupId>com.adobe.aem</groupId>
<artifactId>aem-sdk-api</artifactId>
<version>${aem.sdk.api.version}</version>
<scope>provided</scope>
</dependency>
<!-- Core Components Dependency -->
<dependency>
<groupId>com.adobe.cq</groupId>
<artifactId>core.wcm.components.core</artifactId>
<version>${core.wcm.components.version}</version>
</dependency>
<!-- Testing Dependencies -->
<dependency>
<groupId>org.junit</groupId>
<artifactId>junit-bom</artifactId>
<version>5.9.3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
Maven Scopes in AEM Development
| Scope | Meaning in AEM Architecture | Examples |
|---|---|---|
provided | The dependency is required for compilation but must not be packaged into the bundle JAR because the AEM OSGi runtime container already exports it at runtime. | aem-sdk-api, org.osgi.service.component.annotations, javax.servlet-api. |
compile | The dependency is bundled or resolved as part of the compilation classpath. Third-party utility libraries (e.g., Apache Commons Text) marked compile must be embedded into the bundle using bnd instructions. | Custom utility libraries, domain SDKs. |
test | Required only for compiling and executing unit test suites; omitted from all production artifacts. | junit-jupiter, mockito-core, io.wcm.testing.aem-mock.junit5. |
Exam Trap: Marking
aem-sdk-apiwith<scope>compile</scope>instead of<scope>provided</scope>is a catastrophic error. Maven will attempt to package hundreds of megabytes of AEM internal platform classes into your OSGi bundle, triggering severe classpath collisions,ClassCastExceptionfailures, and OSGi bundle resolution errors upon deployment.
Which module in an AEM Project Archetype multi-module reactor is responsible for compiling Java business logic, Sling Models, and OSGi services into an OSGi bundle JAR?
Why does the root pom.xml declare the Adobe Experience Manager Cloud Service SDK dependency (aem-sdk-api) with <scope>provided</scope> in <dependencyManagement>?
A developer needs to create a new client-side JavaScript and SCSS build pipeline using Webpack and TypeScript. In a standard AEM Project Archetype, where should the source files live, and how do the compiled assets reach AEM?