I felt it was about time I found a moment to write this article. I am sure that more than one frontend developer (especially in the role of tech lead or frontend architect) working in AEM projects, will find it useful. It's very important you understand that the advise I give here is my sole opinion and preferences, but it's backed up by 5 years of experience as Frontend Tech Lead in large enterprise platforms built on top of AEM.
Like every architecture topic, it is a bit dense, so I will split it into different subtopics, for it to be easier to follow.
I will presuppose that the whole project is structured following the Maven Archetype https://maven.apache.org/guides/introduction/introduction-to-archetypes.html (or the Adobe Archetype, in the best of cases), and obviously, that Maven is the compilation tool to put it together. Other assumptions, you're using at least version 6.3 of AEM, Maven 3.5+, Java8, and Sling Models. (Although the backend tech matters little, to be honest, the Maven version does not)
In the frontend, our stack will be ES6 (no frameworks for now, but we will integrate one at a later step -aka a new post- ), we will use Redux for state management, and obviously our html will be written as HTL templates. Ready to go?
The first thing you need to consider, is your folder structure. And to decide your folder structure, you may need to work together with your backend tech leads or solution architect.
Usually they will have a strong opinion on the subject, and the (client) requirements will play a major role. Let's work on the hypothesis of having a large enterprise project between hands. That will imply perhaps, a lot of sub-projects under a common dependency. Each one of those, we will call 'tenants'.
So let's propose the following structure (I know it will look familiar to people working with AEM, so I won't go into details).
tenants is a module on its own.
If you've been working with AEM for a while, as a frontend or backend developer, you're very likely familiar with AEM's pattern for frontend code, the client-side libraries (known by AEM devs as clientlibs). You can read more about them here -> https://helpx.adobe.com/experience-manager/6-4/sites/developing/using/clientlibs.html
Clientlibs are categorised, (precisely by declaring a category name for each one of them), and you can have as many categories as you wish (hold that thought!). AEM as a system downloads its own core clientlibs, (think granite for example, that makes all authoring interface dialogues work etc). This clientlib, has a dependency on jQuery, so you have jQuery available in the frontend if you need it, even if you don't introduce it yourself. (Of course, you're restricted to a particular version!).
Remember when I said you can have all the clientlibs you wish to have? Well, in theory, you can. In practice, you probably want to make that decision depending on the protocol that will be serving your site. If it's http/2, (and I encourage you to fight for that!) you may have a clientlib category per component or module. Conversely, if you're on HTTP 1.1, then you are probably better aggregating all your bundles into one, and serving them as a single category per 'wcmmode' (author or preview/publish). That's an important decision at the time of designing your frontend build. So you please make that question early to your DevOps team or architect. We will speak about aggregation and compression, in a later post.
Please notice that AEM supports compilation of LESS natively. But we won't use it. We will do our CSS pre processing on our own.
In the latest versions of AEM, it is a good practice to classify components per content or structure, meaning whether they have a structural function or will be dragged and dropped into the content. I believe that's a great utility to understand what may be a common asset or component, and what serves a more dedicated, specific role.
With that in mind, let's look at what the structure inside of a tenant may look like:
Additionally, inside of each component folder, you would have a
- componentname.config.js (optional when necessary)
Earlier in this post we mentioned these categories and explained that they're recommended to exist in a common dependency. By shared and abstract we mean for example utility code such as mixins, functions, maps, and global variables that serve for the purpose of making those work.
Since this code is consumed by all tenants, we will place it in the components package. Not only it makes 0 sense to repeat this code several times, it does not make sense to maintain it several times. It also does not belong in the tenants space, where only tenant specific code must be.
Now we have added all these, our structure looks something like the one below.
Now we can finally get to the fun part. Installing and configuring our frontend build. I recommend this global frontend build to be in a separate module. If you want to know more about it (what technologies we will be using, how we will configure it to grab our entries, etc), read the next post, !
And while I write it, you can watch this video: