.article-header { text-align: center; margin-bottom: 3.5rem; } .article-tag { display: inline-block; background: var(--tag-bg); color: var(--tag-color); font-size: 11px; font-weight: 600; letter-spacing: 1.5px; text-transform: uppercase; padding: 5px 12px; border-radius: 6px; margin-bottom: 1.5rem; } .article-title { font-size: clamp(2rem, 4vw, 3rem); font-weight: 800; line-height: 1.2; margin-bottom: 1.5rem; color: #fff; } .article-meta { font-size: 0.9rem; color: var(--muted); display: flex; align-items: center; justify-content: center; gap: 12px; } .article-meta span { display: flex; align-items: center; gap: 6px; } .article-content h2 { font-size: 1.8rem; font-weight: 700; margin: 3rem 0 1rem; color: #fff; } .article-content h3 { font-size: 1.4rem; font-weight: 600; margin: 2rem 0 1rem; color: #fff; } .article-content p { margin-bottom: 1.5rem; font-size: 1.05rem; } .article-content ul, .article-content ol { margin-bottom: 1.5rem; padding-left: 1.5rem; font-size: 1.05rem; } .article-content li { margin-bottom: 0.5rem; } .code-block { background: #0a0f1e; padding: 1.5rem; border-radius: 12px; font-family: 'JetBrains Mono', monospace; font-size: 0.85rem; color: #8899bb; line-height: 1.6; overflow-x: auto; margin-bottom: 2rem; border: 1px solid var(--border); } .kw { color: #a78bfa; } .fn { color: #6382ff; } .str { color: #7dd3b0; } .cm { color: rgba(136,153,187,0.45); } .ty { color: #eab308; } .newsletter-box { background: linear-gradient(135deg, rgba(99,130,255,0.08), rgba(167,139,250,0.08)); border: 1px solid rgba(99,130,255,0.25); border-radius: 20px; padding: 2.5rem; text-align: center; margin-top: 5rem; } .newsletter-box h3 { font-size: 1.3rem; font-weight: 700; margin-bottom: 0.5rem; color: #fff; } .newsletter-box p { font-size: 0.9rem; color: var(--muted); margin-bottom: 1.5rem; } .nl-form { display: flex; gap: 8px; flex-wrap: wrap; justify-content: center; } .nl-form input { flex: 1 1 200px; background: rgba(255,255,255,0.05); border: 1px solid var(--border); border-radius: 10px; padding: 0.65rem 1rem; color: var(--text); font-size: 0.9rem; outline: none; } .nl-form input:focus { border-color: var(--accent); } .nl-form button { background: linear-gradient(135deg, var(--accent), var(--accent2)); border: none; border-radius: 10px; padding: 0.65rem 1.4rem; color: #fff; font-size: 0.9rem; font-weight: 600; cursor: pointer; transition: opacity 0.2s; } .nl-form button:hover { opacity: 0.85; } footer { border-top: 1px solid var(--border); padding: 1.5rem 2rem; text-align: center; font-size: 0.78rem; color: var(--muted); } footer a { color: var(--muted); text-decoration: none; } footer a:hover { color: var(--accent); }

Sling Models Done Right: From Boilerplate to Clean Architecture

If you have worked with Adobe Experience Manager (AEM) for more than a few months, you have likely encountered massive Sling Models filled with repetitive boilerplate code. You know the ones: endless @Inject annotations, manual null checks, and adaptable declarations that span multiple lines.

It doesn't have to be this way. In this guide, we will explore how to write clean, modern Sling Models for AEM 6.5 and AEM as a Cloud Service (AEMaaCS).

The Problem with Old Sling Models

Historically, developers used the generic @Inject annotation for everything. This caused several issues:

  • Performance Overhead: @Inject forces Sling to evaluate every single injector (ValueMap, Bindings, OSGi services, Child Resources) until it finds a match.
  • Fragility: If an injector fails, your entire model might fail to adapt, causing a NullPointerException in your HTL.
  • Readability: Boilerplate getter methods clutter the class.

The Modern Approach

Instead of @Inject, we should use specific injector annotations. Here is an example of a perfectly structured Sling Model:

package com.mishraventures.aem.core.models;

import org.apache.sling.api.resource.Resource;
import org.apache.sling.models.annotations.DefaultInjectionStrategy;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.injectorspecific.ValueMapValue;

@Model(
  adaptables = Resource.class,
  defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL
)
public class HeroComponent {

  @ValueMapValue
  private String title;

  @ValueMapValue(name = "jcr:description")
  private String description;

  public String getTitle() {
    return title;
  }

  public String getDescription() {
    return description;
  }
}

Why is this better?

By using @ValueMapValue, we explicitly tell Sling to only look in the ValueMap (the properties of the node). This is significantly faster. Furthermore, by using DefaultInjectionStrategy.OPTIONAL, we prevent the model from completely failing if a single non-critical property is missing from the JCR.

Lombok: The Ultimate Boilerplate Killer

If your project allows it, Project Lombok can eliminate the need for getter methods entirely. By annotating the class with @Getter, Lombok generates the methods at compile time, leaving your source code incredibly clean.

However, always ensure that your AEM Maven archetype is properly configured to handle Lombok during the build phase, or you will run into pipeline errors in Cloud Manager.

Summary

Stop using @Inject. Start using specific annotations like @ValueMapValue, @ChildResource, and @OSGiService. Default your injection strategy to optional, and keep your Sling Models strictly focused on retrieving and formatting data for your HTL scripts.