Qi4j – Structure your Applications

For decades, we have viewed software applications as layers and modules, but no-one has yet tried to capture these simple concepts and enforce good practices in the development. Qi4j does, and in this article we will try to explore how this is done and what benefits are achieved.

The Age Old Diagram

Very early software has been designed on paper using modules and layers. We are all very familiar with diagrams like this;

Figure 1

It is Modules being part of Layers, that are stacked on top of each other. Let’s call it the Layered Modules Metaphor (LMM). It is used to communicate an overview of the entire application, without getting the audience bugged down with too much details. By rigorously following the LMM of the project, one benefit from fewer system- wide bugs, lower long-term maintenance cost and often a more flexible system to react to change.

Most projects use the LMM to explain how the application is structured, many try to follow it, but very few projects enforces it, and I think we all have seen horror cases where for instance classes in infrastructure layer uses classes in the web layer.

Providing Structure

Qi4j has taken the bold route to provide explicit support for the LMM, to
discipline the developers on the team. The Qi4j application structure is a small
set of easy rules;

  • Structure
    • All Structure is declared statically at boot time.
    • All Composite instances belong to a Module.
    • All Services belong to a Module.
    • All Modules belong to a Layer.
    • All Layers are on top of zero, one or many other Layers (but not cyclic).
    • All Layers form the Application.
  • Access
    • Modules can access all other Modules in the same Layer.
    • Layers can access the Layers directly below it (not transitive).
  • Visibility
    • Composite instances are by default only visible within the Module.
    • Composite instances can be made visible within the Layer, between Layers and outside the Application.

These sound a lot more complicated than it is in reality. Essentially, Composite instances are private within the Module where they were created, unless explicitly made public either outside the Module, outside the Layer or even outside the Application.

The structure is not optional, but convenience method exist to establish common application structures, including a single module in a single layer.

Using Structure

Domain code does not need to know about the application structure, it is just “there”, but it shows up in the form of the @Structure annotation. Example;

public class SoupProductionMixin
    implements SoupProduction
    @Structure CompositeBuilderFactory factory;
    public Soup createSoup( ManyAssociation ingredients )
        CompositeBuilder builder =
            factory.newCompositeBuilder( SoupComposite.class );
        // initialize the Soup composite with the given ingredients.
        SoupComposite soup = build.newInstance();
        return soup;

The CompositeBuilderFactory will be injected to the mixin upon creation.

Another common case where Structure comes into play is when a Service is looked up. If one and only one Service of the requested type is found in the same Module, then no additional assembly will be required. Using Services becomes extremely simple;

public class SoupInventoryMixin
    implements SoupInventory
    @Service SoupProduction producer;
    @Service GenericInventory inventory;
    public Soup getSoup( SoupType type, int quantity )
        // logic, get from inventory,
        // if inventory low, produce more

For instance, if a GenericInventory service is declared in the Bread Module, each inventory service instance will be bound by its proximity to its respective client. (More about Qi4j Services in the next issue of Jayview.)

Establishing Structure

Qi4j applications need to be bootstrapped by application code. The simplest startup possible looks like this;

public class MyApplicationAssembler
    implements Assembler
    public void assemble( ModuleAssembly module )
        throws AssemblyException
        module.addComposites( HelloWorldComposite.class );
Assembler assembler = new MyApplicationAssembler();
Application app = new ApplicationFactory().newApplication( assembler );
HelloWorld helloWorld = factory.newComposite( HelloWorld.class ); 

Another way to write this is to use the SingletonAssembler;

SingletonAssembler assembler = new SingletonAssembler()
    public void assemble( ModuleAssembly module )
        throws AssemblyException
        module.addComposites( HelloWorldComposite.class );
CompositeBuilderFactory factory = assembler.compositeBuilderFactory();
HelloWorld helloWorld = factory.newComposite( HelloWorld.class );

The SingletonAssembler is a convenience class that creates a Qi4j application as a single module in a single layer.

Figure 2

It is also possible to pass an Assembly[][][] to the newApplication() method, which will create an application context with a “pancake” layering, i.e. each layer is on top of one layer and below one layer (except the first and last one). For instance;

Assembly[][][] assembly = new Assembly
{ // Application
    { // Layer 1
        { module1Assembler } , // Module 1
        { module2Assembler } // Module 2
    { // Layer 2
        { module3Assembler } // Module 3
Application app = new ApplicationFactory().newApplication( assembly );

This will give the following structure;

Figure 3

And finally, for arbitrarily complex application structures, one need to pass an ApplicationAssembly instance to the newApplication() method. The ApplicationAssembly is used by iteratively building up the LayerAssemblies and from them iteratively building up the ModuleAssembly. Example;

ApplicationAssembly app = new ApplicationAssembly();
LayerAssembly domainLayer = appAssembly.newLayerAssembly();
ModuleAssembly customerModule = domainLayer.newModuleAssembly();
LayerAssembly webLayer = appAssembly.newLayerAssembly();
ModuleAssembly customerWebModule = webLayer.newModuleAssembly();
webLayer.uses( domainLayer );
LayerAssembly remotingLayer = appAssembly.newLayerAssembly();
ModuleAssembly customerRemotingModule = remotingLayer.newModuleAssembly();
remotingLayer.uses( domainLayer );
Application app = new ApplicationFactory().newApplication( assembly );

which produces a structure like;

Figure 4

Benefits of Structure

The two main benefits from explicit coding of applications are;

1. Resolution by Proximity.
2. Architectural Enforcement.

What this means is, composites that are nearby are easily accessed, and that nearby composites will have higher priority, and composites private to a module or a layer is not accessible from the outside. Since services are composites, the service resolution becomes more implicit and requires a lot less assembly configuration.

Another interesting side-effect of the Structure concept in Qi4j is that every application has a static structural composition, which can be extracted and visualized by tools, instead of maintained separately. This enables architects, designers and team leads to track whether developers follow the architecture, and not trying to circumvent it.

Concluding Structure

We have viewed applications in conceptual layers and modules for decades. In reality, the difference between the actual codebase and the ideal view of the architecture deteriorate over time. Qi4j allows architecture enforcement and visualization of actual structure, leading to better over-all quality. It also communicates to developers, both new and old members of the team, what the intent is and the bigger picture of the application, without resorting to externally maintained documents and diagram that has a tendency to be out of date after a few months. Again, Qi4j takes an age-old metaphor and re-invents its usage.

Niclas Hedhman

Originally published in JayView.

Leave a Reply

Close Menu