Future-proof your architecture with aspects

Using aspects you can safeguard that the code will keep in a good state after refactoring

How do we safeguard that new features will comply with the new design? Using aspects you can future-proof the architecture and code after refactoring.

A refactoring aims to leave the code in a better state than before. But what happens after the dust settles and the team begins new development that must follow the new architecture? What can we do in order to avoid degeneration of the software architecture?

Documentation & training

You need to update all relevant documentation, such as the SAD and design documents. Developers also need hands-on training to understand the refactoring.

Code documentation: adding comments describing what an interface is doing so we know what to do when implementing.

This and other things are all done in order to keep code from deteriorating. But is it really enough? What happens for example if new developers join? How do we guarantee that they will acquire the knowledge needed?

More often than not, developers code first and read documentation later. So what we need is a way to enforce our architectural refactoring (or any architecture for that matter) and convey that to the developer. Our answer is aspects! First some background.

Aspects

Aspects separate cross-cutting concerns, isolating business logic from secondary functions like caching and logging. Developers define aspects in separate modules, specifying their behaviour and the points in the program where they should trigger. Using this approach you don’t have to concern yourself with coding logging or caching in every business object; the aspect will handle this.

Even though aspects are great at doing this, how does this relate to future-proofing our architecture? Of course, when defining our aspects correctly we will always have logging, caching, etc., without worrying about whether the programmer remembered to add it. But it will not check that design patterns are followed correctly. We want to use it where it hurts for a programmer: at compilation time.

AspectJ

You can achieve this using the AspectJ framework, an extension of the Java programming language that implements the aspect model. It has all of the functionality mentioned above, plus the ability to use point-cuts to add compile-time warnings and errors with the declare statement. This can be a very efficient way of reinforcing an architectural rule.

Further reading of AspectJ: https://www.eclipse.org/aspectj/

Diagram of the AspectJ compiler (ajc) taking Java source files (.java) and aspect declaration files (.aj) as input, and producing weaved bytecode and bytecode for aspects (.class) as output.

Using the declare statements of AspectJ you can control that:

certain calls from specified classes are forbidden
methods follow requested signatures
certain calls from specified classes are mandatory
and more…

For example:

aspect ArchitectureEnforcement {
declare error : call(* DataLayer.*(..)) && !within(TrustedClasses+) :
"Direct access to DataLayer is not permitted outside trusted classes.";
}

This means that the aspect will generate a compiler error when this pattern is encountered. The condition (here TrustedClasses) can be whatever you can define using aspects.

You can also declare exceptions to the rules: specific classes or methods can be excluded from the compile-time check. It is a powerful tool and you can specify almost any compilation rule you want to enforce.

Using aspects you can safeguard that the code will keep in a good state after refactoring. Separating concerns helps; with the addition of compile-time checks we can take a step further. It is hard to ignore compilation errors.

Daniel Svedberg, Senior Consultant
Daniel Svedbergis a senior developer and software architect with experience building large, mission-critical systems in multi-layered architectures.
Scroll to Top