AEM: multi-tenant monorepo

2022-04-08

Recently I was looking into the codebase of an AEM project me and my team started working on more than 2 years ago. It felt to me that the structure of the project although supporting multitenancy was far from optimal. I decided to look into a better approach and in this post I am sharing my findings with you.

Multitenancy

Multitenancy is a reference to the mode of operation of software where multiple independent instances of one or multiple applications operate in a shared environment. The instances (tenants) are logically isolated, but physically integrated.

- https://www.gartner.com/en/information-technology/glossary/multitenancy

In terms of AEM this means managing multiple websites on a single instance.

Monorepo

In version control systems, a monorepo is a software development strategy where code for many projects is stored in the same repository. This software engineering practice dates back to at least the early 2000s, when it was known as a 'shared codebase'. A related concept is the monolith, but while a monolith combines its sub-projects into one large project, a monorepo may contain independent projects.

- https://en.wikipedia.org/wiki/Monorepo

In my case every tenant lives inside a maven module allowing tenant teams to work in parallel. One can deploy an entire site (locally) without ever needing to deploy another tenant.

Overview

I have used the Frituur in a demo before, but such iconic place deserves being used as often as possible.

aem-multi-frituur

  • apps: hidden base components, author clientlibs and toolbar overlays
  • core: base sling models & Vite related tag generation
  • config: repository initializer script, user mapping & Vite related configuration

haag-4 / penske-vol

  • apps: proxy components & generated clientlib(s)
  • frontend: tenant specific styling & generation of clientlib(s)
  • config: local configuration to create the link between a clientlib and the Vite dev server
  • content: tenant specific templates & content
  • core: potential extend of the base components using the delegation pattern

ui-common

  • dom: dom related util methods
  • vue: Vue 3 bootstrapping logic & components

If you would like to learn more on how Vite is exactly integrated, take a look at my Vite series: part 1 & part 2.

Theming

I am trying to keep the CSS as simple as possible hence the use of CSS variables making it easy for other tenants to provide their own customisation. Let me show you this using the Heading component as an example.

ui-common/components/heading

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
:root {
    --heading-margin-bottom: 40px;
    --heading-border: 10px;
    --heading-border-color: black;
    --heading-image-max-width: 500px;
    --heading-radius: 0px;
}

.heading-component {
    display: flex;
    justify-content: center;
    margin-bottom: var(--heading-margin-bottom);

    & img {
        border: var(--heading-border) solid var(--heading-border-color);
        border-radius: var(--heading-radius);
        max-width: var(--heading-image-max-width);
        width: 100%;
    }
}

tenant.haag-4/frontend/src/styling/heading.css

1
2
3
4
5
6
7
@import "@jeroendruwe/ui-common/src/vue/components/heading/index.css";

:root {
    --heading-border: 20px;
    --heading-border-color: #ceaa00;
    --heading-radius: 200px;
}

Please note that I am using postcss-nesting here allowing for the nesting of CSS similar to Sass. I am using IntelliJ and I needed some extra configuration in order for the CSS file to be edited without any errors popping up. Your editor of choice might require something similar.

Toggle dev server

I've added a tiny feature to the main editor toolbar which makes it possible to toggle the devServerEnabled flag at: be.jeroendruwe.aemmultifrituur.core.internal.services.DevServerServiceImpl.cfg.json. It allows the developer to switch to actual clientlibs instead of communicating with a dev server, it can be used like this:

All of the information and code shared above can be found on Github. If you have any questions, do not hesitate to contact me or leave a comment below.

Created by Jeroen Druwé