# AEM Project Structure Explained

*Series: AEM Foundations — Part 5*

When you generated a project from Adobe's Maven archetype earlier in this series, it created several modules with cryptic-looking names — `core`, `ui.apps`, `ui.content`, `ui.config`, and more. This post breaks down what each module is for and why AEM projects are structured this way.

## Why AEM Projects Are Multi-Module

Unlike a typical web app where everything might live in one deployable unit, AEM separates **code**, **content**, and **configuration** into distinct Maven modules. This separation exists because these three things have very different lifecycles:

*   Code changes with every feature or bug fix
    
*   Content (sample pages, component defaults) is set up once and rarely redeployed
    
*   Configuration (OSGi settings) often differs between environments (dev, stage, prod)
    

Keeping them separate means you can deploy code changes without accidentally overwriting content, and manage environment-specific configuration independently.

## The Core Modules

### `core`

This is where your **Java code** lives — Sling Models, Servlets, OSGi services, schedulers, and any custom business logic. If you're writing Java for AEM, this is almost always where it goes. It compiles into an OSGi bundle that gets deployed into AEM's runtime.

### `ui.apps`

This holds everything that goes under `/apps` in the JCR repository:

*   Component definitions (dialogs, HTL scripts, `.content.xml` files)
    
*   Client libraries (CSS/JS, bundled as `clientlibs`)
    
*   Templates and template policies
    
*   Any other application-level structure
    

Recall from the previous post in this series: `/apps` is where you place your customizations, which Sling looks up before falling back to Adobe's defaults under `/libs`. This module is effectively "your app's definition" in the repository.

### `ui.content`

This contains **sample or default content** — example pages, folder structures under `/content`, and sometimes reference content used for demos or initial setup. In many real projects, this module is either minimal or excluded from production deployments entirely, since live content is authored directly in the Author environment rather than deployed via code.

### `ui.config`

This holds **OSGi configuration** — settings for things like PDF processing, workflow launchers, or any configurable service, often split by run mode (author vs. publish, dev vs. prod) so the same codebase behaves correctly across environments.

### `ui.frontend` (in newer archetype versions)

Newer AEM projects often include a dedicated frontend module using modern tooling (webpack, npm) to build client-side assets, which then get packaged into `ui.apps` clientlibs during the build. This lets front-end developers work with familiar JS/CSS tooling rather than hand-writing clientlib folder structures.

### `it.tests` (optional)

Some projects include an integration test module here, used for automated tests that run against a deployed AEM instance as part of CI/CD pipelines.

### `all`

This is a "wrapper" package that bundles `core`, `ui.apps`, `ui.content`, and `ui.config` together into a single deployable content package. When you ran `mvn clean install -PautoInstallSinglePackage` in the previous post, this is typically the package that actually got installed onto your local instance.

## A Simplified Visual

```plaintext
my-project/
├── core/            → Java code (Sling Models, Servlets, OSGi services)
├── ui.apps/         → /apps content: components, clientlibs, templates
├── ui.content/      → /content sample or reference content
├── ui.config/       → OSGi configurations per run mode
├── ui.frontend/     → Modern frontend build tooling (if present)
├── it.tests/        → Integration tests (optional)
└── all/             → Combined deployable package
```

## Why This Structure Pays Off Long-Term

This separation might feel like overhead on a small project, but it becomes essential as a project grows:

*   Multiple developers can work on `core` (Java logic) and `ui.apps` (components/front-end) simultaneously with minimal conflict
    
*   Environment-specific behavior lives cleanly in `ui.config`, instead of scattered `if (environment == "prod")` checks in code
    
*   CI/CD pipelines can build and deploy the `all` package as a single unit, while still keeping the underlying modules independently testable
    

## Series Wrap-Up

That completes the **AEM Foundations** series. At this point you should understand:

*   What AEM actually is, and how Sites, Assets, and Forms differ
    
*   The Author/Publish/Dispatcher architecture and how requests flow through it
    
*   How to set up a local AEM SDK environment
    
*   How Sling resolves resources and processes requests
    
*   How a real AEM project is organized on disk
    

From here, the **AEM Core Development** series picks up with hands-on component building — starting with your first HTL component and Sling Model.
