Sling Models Best Practices for Maintainable AEM Code

By Alberto Zarza Martín · · 7 min read

Sling Models are simple enough to misuse without noticing for a long time. These are the practices that make the difference between a codebase that stays maintainable and one that becomes fragile as it grows.

Be explicit about injection strategy

Default injection ordering (resource, then request, then OSGi service, and so on) is convenient but can silently pick up the wrong value if two sources expose a property with the same name. Set @Inject defaults or specify the injector explicitly for anything business-critical rather than relying on the default resolution order.

Keep adaptables narrow

A model adaptable from both Resource and SlingHttpServletRequest is convenient, but it invites logic that behaves differently depending on adaptable type in ways that are easy to miss during review. Prefer one adaptable per model unless there is a genuine reuse need, and document the reason when you do combine them.

Push logic out of exporters

When exposing models via the Sling Model Exporter for JSON/GraphQL consumption, keep exporter-facing getters thin. Business logic belongs in dedicated methods that are unit-testable outside of a running Sling context — this alone makes the biggest difference to how testable a codebase actually is.

Cache expensive computations at the model level, not per-getter

If a getter does non-trivial work (a query, a service call), compute it once in @PostConstruct and store the result, rather than recomputing it every time the getter is called from a template. Templates often call getters more than once per render.

Testing

None of this is exotic — the value comes from applying it consistently across a large team, since Sling Models are usually the single most numerous class type in an AEM codebase.