Problem framing
If you are kicking off a fresh AEM site this month you are probably staring at a blank repo and a lot of choices. Do we start from the AEM Project Archetype or roll our own folder layout. Do we go all in with Sightly and Sling Models or keep a few JSPs around for comfort. Where do front end builds live. The clock is ticking and everyone wants to see a hero banner on publish by next week.
The official Maven AEM project archetype gives you a decent baseline for AEM 6.2. It sets up a multi module build with a core bundle, ui.apps, ui.content, dispatcher files and integration tests. It wires the content package maven plugin so you can ship code and content safely. It is opinionated in a good way, but not perfect. You will still need to make a few early calls that shape the next twelve months of your project.
Quick recap: AEM 6.2 is what most teams are rolling out right now. Sightly is stable. Sling Models are in a good place. The archetype is on version 10 at the time of writing.
Patterns and anti patterns
Lean multi module setup. Keep the modules that ship code small and focused. A common shape from the archetype is core, ui.apps, ui.content, it.tests, dispatcher. That separation keeps your OSGi bundle clean and your content packages predictable.
Sightly with Sling Models. Put rendering in Sightly and move logic into Sling Models. Keep JSPs out of new code. You will thank yourself when authors ask for tweaks during UAT. A simple data class is easy to test and easy to read.
Clientlibs first. Use categories and dependencies wisely. One category per component library, and one site level category that pulls the stack together. Keep third party libraries in a vendor folder so upgrades do not turn into a scavenger hunt.
Run mode aware config. Put OSGi configs under /apps/<project>/osgiconfig and split by run mode. Local, dev, stage, prod. Keep secrets out of Git. Ship defaults that are safe and let the ops team overlay the sensitive parts.
Dispatcher as code. Treat rewrite rules and cache rules as first class. The archetype gives you a dispatcher folder for that reason. Version it. Review it. Test it in CI with a tiny publish and a container for dispatcher.
Avoid these traps:
- Packing sample content and demo components into your main filter rules. Keep demo content in a separate package or you will ship it by accident.
- Mixing business logic into JSPs. Move logic into Sling Models and services.
- Giant filter rules that grab half of /etc and /content. Be surgical. Only include what your app owns.
- Embedding third party bundles into your core bundle just to make it build. Use proper OSGi dependencies.
- Letting the front end build live outside Maven with no link back. Wire gulp or grunt into the Maven lifecycle so the build is one command.
Case vignette
A small team at a retail client stood up a greenfield site last month. They started from the Adobe archetype and trimmed what they did not need. One person owned the core bundle, another owned components and clientlibs, and a third watched dispatcher and CI. Day three they had author and publish running on a laptop with sightly components showing data from a Sling Model.
They generated the project like this:
mvn -B archetype:generate \
-D archetypeGroupId=com.adobe.granite.archetypes \
-D archetypeArtifactId=aem-project-archetype \
-D archetypeVersion=10 \
-D groupId=com.retail \
-D artifactId=storefront \
-D version=1.0.0-SNAPSHOT \
-D package=com.retail.storefront \
-D appsFolderName=storefront \
-D artifactName="Storefront"
The result looked like this:
storefront/
core/ <-- OSGi bundle
ui.apps/ <-- components, clientlibs, osgi configs
ui.content/ <-- site content and templates
it.tests/ <-- selenium plus pax exam bits
dispatcher/ <-- vhost, farm, rewrites
all/ <-- optional convenience package
They pushed for Sightly and Sling Models from the start:
// core/src/main/java/com/retail/storefront/models/TeaserModel.java
package com.retail.storefront.models;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.DefaultInjectionStrategy;
import org.apache.sling.models.annotations.injectorspecific.ValueMapValue;
@Model(adaptables = Resource.class, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)
public class TeaserModel {
@ValueMapValue private String title;
@ValueMapValue private String link;
public String getTitle() { return title; }
public String getLink() { return link; }
}
<!-- ui.apps/src/main/content/jcr_root/apps/storefront/components/teaser/teaser.html -->
<div class="c-teaser" data-sly-use.model="com.retail.storefront.models.TeaserModel">
<a class="c-teaser__link" href="${model.link @ context='uri'}">
${model.title}
</a>
</div>
For front end they kept it simple. Gulp compiles sass into a clientlib folder that the ui.apps module packages. Maven calls gulp in the build so the CI job stays honest.
<!-- ui.apps/pom.xml fragment -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<executions>
<execution>
<id>gulp-build</id>
<phase>generate-resources</phase>
<goals><goal>exec</goal></goals>
<configuration>
<executable>npm</executable>
<arguments>
<argument>run</argument>
<argument>build</argument>
</arguments>
</configuration>
</execution>
</executions>
</plugin>
They also kept OSGi configs tidy with run modes:
ui.apps/src/main/content/jcr_root/apps/storefront/osgiconfig/config.author/
org.apache.sling.commons.log.LogManager.factory.config-storefront.cfg.json
ui.apps/src/main/content/jcr_root/apps/storefront/osgiconfig/config.publish/
com.day.cq.replication.impl.ReplicationTransportHandlerFactory.cfg.json
The team shipped a first slice to stage in two weeks. Clean build. Clean package. No surprise filters.
Lessons learned
- Start from the archetype then prune. It is easier to delete than to add when the team is under pressure.
- Keep configs near the code. Run modes plus clear naming go a long way during a late night prod push.
- Sightly pays off. Authors get predictable markup and developers keep logic out of views.
- Make dispatcher part of the project. Treat it like code. Version it. Test it.
- Guard your filter rules. Only include what you own. Review filters in every pull request.
- One build command. Wire front end into Maven so new laptops do not become a rite of passage.
- Adopt a common toolbox. ACS AEM Commons and Sling Models annotations save time and make the code base readable across teams.
If you are about to bootstrap your repo, take the archetype for a spin and shape it to your needs. Pick solid defaults, keep the surface small, and invest early in a repeatable build. Your future self and your authors will be happy.
Creation date: 2016 07 09 00:21:35