After working some time within the software industry, you get a feeling for good software architecture. Or, to be more honest, you get a creeping feeling when the architecture is really bad. That is when the code is tangled like a Gordian knot. After some futile refactoring attempts, you consult the software architect at your company and you will be given a design document stating the architectural principles that should be obeyed during software development. It is a nifty piece of paper and you can tell by looking at it that someone has spent a lot of time working out how the software should be structured. The bad news is that it has little resemblance of the current state of the code base.
Recipe
So how can you shape up the code? Yet better, how can you prevent that the code turns into spaghetti in the first place? One way of looking at architectural requirements is that they are crosscutting concerns that are scattered throughout the software. As such, they can be implemented and enforced by using AOP, aspect-oriented programming. The recipe is pretty straight forward:
- Implement a pointcut that finds the violations of your architecture.
- Implement an advice that notifies you about the violations.
- Wrap the pointcut and the advice into an aspect.
- Refactor your code and exercise your aspect until all architectural violations have been removed.
Example
This recipe of using aspects as a way of enforcing architectural rules can be applied in any kind of project, for example to enforce the MVC pattern, to separate one domain from another in a DDD project and so on. In this particular example, the application is based on a three layer architecture. The top layer being the GUI layer, the middle layer is the service layer and then there is the DAO layer in the bottom. Each layer has a separate package, as stated below:
SomeGui.javapackage com.jayway.application.gui; /** Simplistic GUI */ public interface SomeGui { /** Renders the GUI */ void render(); } SomeService.javapackage com.jayway.application.service; /** Simplistic Service */ public interface SomeService { /** Executes some service */ void service(); } SomeDao.javapackage com.jayway.application.dao; /** Simplistic DAO */ public interface SomeDao { /** * Finds something in the DAO * @return some data */ public Object find(); } |
![]() |
Architectural Rules
Four architectural rules have been defined:
- The GUI layer must not access the DAO layer
- The Service layer must not access the GUI layer
- The DAO layer must not access the Service layer
- The DAO layer must not access the GUI layer
These rules are the candidates for defining the pointcuts that should be implemented. An example of code that would violate the first rule is:
BadGuiImpl.java
/** * Bad GUI implementation that violates the architectural rule * because it calls a method in the DAO layer */ public class BadGuiImpl implements SomeGui { private SomeService someService; private SomeDao someDao; @Override public void render() { // it is ok to use the service... someService.service(); // ...but it is not ok to call the DAO directly someDao.find(); // more rendering } }
Using AspectJ, two pointcuts have been implemented to trap the violation. Additionally, AspectJ also provides the @DeclareError
annotation that can be used for the advice implementation. Finally, an aspect that comprises the pointcuts and the advice has been created:
ArchitecturalEnforcement.java
/** * The aspect that is responsible for architecture enforcement: * The GUI layer must not access the DAO layer. */ @Aspect public class ArchitecturalEnforcement { /** Pointcut for finding join points inside the GUI layer */ @Pointcut("within(*..*gui..*)") public void withinGui() {} /** Pointcut for finding method calls to the DAO layer */ @Pointcut("call(* *..*.dao..*(..))") public void callDao(){} /** Advice that defines an error when a GUI method calls a method in the DAO layer */ @DeclareError("withinGui() && callDao()") private static final String GUI_MUST_NOT_USE_DAO = "GUI must not access DAO"; }
Exercise the Aspect
How should you use your aspects to enforce the architecture? Since we now have the tools to automate the architectural review, you should use them frequently. AspectJ has support for compile time weaving which means that the advices can be woven into their corresponding join points during source code compilation. The aspectj-maven-plugin can do it for you:
pom.xml
... org.aspectj aspectjrt 1.6.7 org.codehaus.mojo aspectj-maven-plugin 1.3 1.6 compile test-compile
Result
If you have put everything together correctly, you will find that you will get a compile time error when you attempt to execute mvn compile
:
[[INFO] [aspectj:compile {execution: default}] [ERROR] "GUI must not access DAO" [INFO] ------------------------------------------------------------------------ [ERROR] BUILD ERROR [INFO] ------------------------------------------------------------------------ [INFO] Compiler errors: error at someDao.find(); ^^^^^^^^^^^^^^ /home/mattias/architectural-enforcement/src/main/java/com/jayway/application/gui/BadGuiImpl.java:20:0::0 "GUI must not access DAO" see also: /home/mattias/architectural-enforcement/src/main/java/com/jayway/application/aspects/ArchitecturalEnforcement.java:1::0
You can also see the error in your Eclipse IDE if you are using the AJDT – AspectJ Development Tools plugin:
Notably, the implementation above was just one of the stated rules. The implementation of all four rules together with some examples that break them and the Maven pom file are available for download for your convenience.
Considerations
There are some things that you may want to consider before introducing aspects as a tool for automated architectural enforcement:
-
Error or Warning
A compile time error is a powerful tool that prevents the developer to commit any code that does not conform to the architectural rules (presumed, of course, that the code is actually compiled before being checked in). A less brutal way of introducing aspects as a part of architectural review is to use the
@DeclareWarning
annotation rather than the@DeclareError
that was used in the example. Consequently, any architectural offenders will be punished with a compiler warning rather than a compiler error. -
Performance
The compile time will increase when you add more architectural rules that should be obeyed, that is when you add more pointcuts. Likewise, the compile time will also increase when your code base grows, because of the increasing number of join points in the code. By limiting the
aspectj-maven-plugin
to certain maven profiles, the developers only have to verify that their particular module conforms to the rules. Alternatively, all modules can be verified by the integration server during nightly builds. The drawback is that the advantage of having the architecture enforced before the code is committed to the version control system will be lost. -
Limitation
The aspect above can only trap architectural violations when a method is being called. Regrettably, any unused declaration that would violate the architecture will pass unnoticed:
AnotherBadGuiImpl.java
package com.jayway.application.gui; import com.jayway.application.dao.SomeDao; /** * Another bad GUI implementation that violates the architectural rule * because it has references to the DAO layer. * However, these errors will remain undetected by AspectJ. */ public class AnotherBadGuiImpl implements SomeGui { /** Unused DAO reference */ private SomeDao someDao; /** * Setter method that for some obscure reason adds a DAO to the GUI * @param someDao A DAO reference that is not found by the pointcut */ public void setDao(SomeDao someDao) { this.someDao = someDao; } @Override public void render() { // valid gui rendering that does not use the dao reference } }
One solution is to create another pointcut, such as
@Pointcut("set(*..*.*dao*..* *)")
, that traps the assignment of thesomeDao
member variable.
Wrap Up
Education of the developers and repeated manual code reviews have been the traditional ways of improving software architecture. Unfortunately, it is not good enough. It is always a good idea to have skilled employees, but even experts do make mistakes. After all, people that manually review code are only humans, which implies that the reviews are resource demanding, yet error prone. With the powerful tools of today’s IDEs it is very easy to do refactoring hastily and soon the code starts to degrade. With a proper implementation, AspectJ offers one way to automate architectural enforcement, hereby preventing architectural drift.
Edit
2010-04-16: Added screenshot of AJDT plugin and an example of how the “set” pointcut can be used.
Nice — I wrote about something very similar a few years back as ‘Layering Enforcement’ on my blog — http://smokeandice.blogspot.com/2007/10/architectural-pattern-layering.html. My solution used annotations and an EJB-style interceptor, which may be appropriate in some scenarios, and could now be used with CDI as well, but may be seen as more intrusive than your solution. Nice work!
M
You can get even more adventurous and decouple your self from the actual instrumentation technology using an event mechanism.
I wrote an article a while back titled “Listening to software as it executes”
http://williamlouth.wordpress.com/2009/01/22/listening-to-software-as-it-executes/
Our approach is much more dynamic though in the example the underlying instrumentation technology is the same but it need not be.
William
@Matt: Thank you! If you like, you can replace the interceptor in your example with a pointcut that finds a marker annotation. All you have to do is to replace the
withinGui()
pointcut in my example with:@Pointcut("within(!@YourMarkerAnnotation *)")
public void missingYourMarkeAnnotation() {}
@William: Interesting project. I guess one would track the package names of the executing classes by using the
BEFORE_BEGIN
andAFTER_END
events (similar to an around advice, but using instrumentation rather than aspects), and throw an exception if any architectural rule is violated?@Matt and @William: I find your articles interesting and they both address my key concern; automation of architectural enforcement. From my point of view, there is an advantage of using compile time verification rather than runtime verification. It enables early architectural enforcement of the entire code base. In other words, the architectural enforcement is decoupled from the need of executing code, let it be tests or the application itself. This would probably not be an issue if project is well managed, but if the project is immature, has poor test coverage or lots of dead code, it is possible that architectural violations will pass unnoticed.
Very cool. I’m going to investigate this for the large legacy project that I’m stepping up as a Solution Architect for. I’ve seen several horrifying examples that is breaking the architecture in that code base.
Have you thought about packaging this as a plug-in and present the information visually in Hudson or Sonar? I think this kind of functionality practically begs to be encapsulated in a plug-in that is easily configurable and resusable across projects.
Magnus
Just like in security you need to combine/layer multiple defense mechanisms so I see a need for both compile time and runtime checking.
I will post a blog entry next week demonstrating how this [your use case] can be easily achieved a number of ways today using events, stack and tierpoint probes provider extensions.
By the way our probe naming is not coupled to Java so we can do this for any JVM supported language including JRuby, Jython, SQL, even HTTP requests. You could even use additional context information related to the current activity.
OH I should point out that our default instrumentation is based on AspectJ though we do offer an Open API which allows multiple instrumentation approaches to be used within the same runtime.
Three Degrees of Separation in APM
http://williamlouth.wordpress.com/2010/03/07/three-degrees-of-separation-in-apm/
Hi Mattias,
nice post. However, you can write pointcuts that declare errors/warning also for situations different than a method call, using the get/set point cuts, or hasField, hasMethod. You could trap if a field containing a dao is accessed (setting or getting it) or if a view class contains a DAO in a field, or even if a view class contains a method that receives in some form a DAO.
Other than static error/warning delcaration, we often emply also runtime checks, that will throw an exception during test execution, in the case where, for example, from a view we “reach” a DAO without going thru the service. Runtime checks can express more complex rules, cause there are situations where the compiler alone cannot determine the application state correctly, while at runtime you have much better support. Since code should be compiled AND tested before checking it in, runtime checks should protect the code as well as static checks.
AOP can indeed be used for architectural policy enforcement; see examples 2 and 4 here:
http://www.javaworld.com/javaworld/jw-04-2002/jw-0412-aspect3.html
However, I think that the 3-Tier example above calls for much simpler means. In this case we should have three modules/projects in our IDE, each having a dependency just on the one it needs. Next, use the power of the build tool (Maven/Ant), by configuring its compile task dependencies accordingly, and cleaning up the classpath of each task accordingly.
@Magnus: I don’t know if there are any specific build server plugins that are particularly useful in this context. The JDepend Hudson plugin will display all dependencies, but as far as I know it has no support for adding architectural rules. If you are an Eclipse user, you can install the AJDT plugin. Once enabled, it will list the errors and warnings in the “Markers” tab, and there will be a notification visible in the margin of the source code if a violation is trapped. Additionally, you may also consider buying a commercial tool that allows you to manage the architecture of your software without caring about the implementation details.
@William: Exciting, I am looking forward to reading your post.
@Simone: Do you have any good links with hasField and hasMethod that you mentioned? I would like to see a working example, because I am not previously familiar with them.
@Hagai: Thank you for the link. The point I am trying to make is that aspects can be used as one tool for architectural enforcement. I deliberately chose a simple example with clear, well-defined rules for illustrational purposes. Agreeably, separating different packages into separate projects and monitoring their dependencies is a good way to promote good architecture, especially if the cuts are as obvious as in this case.
Good post! The limitations of the AOP based architectural enforcement approach can be complemented by static analysis tools such as Structure101 and FindBugs. I talk about all this in my presentation I did last year at JFokus, http://www.infoq.com/presentations/Controlling-Architecture-Magnus-Robertsson
The general idea should be to keep as much of the architectural aspects in the code as possible. There are frameworks attempting to do this such as Qi4J http://www.qi4j.org/ which I find really interesting.
Apologies for the delay but it has been extremely busy the last few months with our OpenCore and Costicity project.
http://williamlouth.wordpress.com/2010/06/09/architectural-enforcement-with-opencore-probes/
Hi,
I was able to do compile time weaving with maven…but with ant, it creates class files from .aj files. However it doesn’t enforce the aspect during compile time
My ant script..
After above task, i compile source files using javac task…
Any guess what i could be doing wrong? I want to enforce aspects during compile time .
@rams: First of all, sorry for the late reply.
It was a long time ago since I last used Ant. As a matter of fact I have never used it together with AspectJ. Nevertheless, here are some thoughts:
The normal javac compiler does not weave the aspects into the source code, so this must be handled separately (that’s why I provided the aspectj-maven-plugin in the blog post). In your case, I guess that you have to run the
AjcTask
or theAjc11CompilerAdapter
as described in AspectJ Ant Tasks of The AspectJ Development Environment Guide.You can also search the AspectJ User Mailing Lists to see if anyone else has similar problems or post a question there if you do not find the answer you are looking for.
Good luck!
Hi,
first of all thank you for this very good article.
I have question:
I’m developing a library to be used within the company where I work and, I want to enforce ‘limiting collaboration’ and ‘factory pattern’ but I have control only on my library and not on the clients of my library, therefore in my case the policies can’t be applied on compile time but must be evaluated only in runtime time.
My question is: can I enforce runtime policies using AspectJ weaving only my library? Can it work? or it’s necessary to weave both clients + my library?
Thank you very much again.
@Indrit: Thank you. There are several possible options, depending on the nature of your project.
First, the simplest solution is to use a default (empty) access modifier for the objects that the factory creates. In order make that possible, you have to place the classes that the factory instantiates in the same package as the implementation of the factory. The advantage being that the constructors are not accessible from any other package, and thus forcing clients to use the factory. Consequently, the architecture is enforced without any aspects.
Secondly, if your colleagues rebuild the entire system including your library, you can still use static compile time enforcement by using some appropriate pointcuts. For example, if your library’s public api is in the
public
package and its implementation is in theinternal
package:Lastly, if you deliver binaries, you could weave your aspects during compile time and then add some notification in runtime like you suggest. Using the same pointcuts as above, you could for example implement a before advice:
Yet again, depending on your project, you should also consider whether or not you should weave in the aspect for production builds. Alternatively, you may choose to implement a different kind of notification when the application runs in production. The reason being that if your library is used by some untested code, it may blow up the entire application if the advice throws an exception.
Hi,
thank you for responding me. A clear and esaustive answer, very useful for me.
I don’t know much of AspectJ but I have a question about the call join-point. As you have showed I have to use the ‘Call’ joint-point matching within the pointcuts. What I know (if I’m not wrong obviously) is that in the case of the call joint-point the weaver weaves only the method invocation locations and therefore I have to weave (compile) also the clients of my library otherwise no advice could be inserted between my-clients and my-library.
As an ‘alternative’ I can use the ‘execution’ join-point, in this case the advice would be available at ‘runtime’ (no need to weave clients, the method body is weaved instead) but in this case it can’t work neither ( I believe ) because you can’t express conditions on the callers.
Therefore my conclusion is that there is no real help from aspectj when:
– you need a ‘runtime’ policy enforcement
– you don’t control your clients (no possibility to weave them)
What I don’t understand is that why I need to apply a ‘policy enforcement’ to code that I already own(compile)?
Would be more useful if I could apply policies at clients that I do not control and I don’t know (runtime)?
Is that correct or I’m doing something wrong?
Thank you very much again.
@Indrit: Unfortunately, compiling the code does not necessarily mean that you own it. I am not encouraging it, but I have seen projects where hundreds of developers work on the same project and they all compiled the entire source code (they did not have a company internal maven repository at that time).
That said, I think I was a bit to fast in hitting the reply button the other day. As you stated, the “call” pointcut that I suggested will not work, because the enclosing code is not available at compile time. Someone may think that the “execution” pointcut would solve this problem, but as you pointed out that will not work either, because when the pointcut traps the thread of execution it is already inside the method itself. Or more formally “
execution(void m()) && withincode(void m())
is the same asexecution(void m())
” as it is expressed in the AspectJ Development Guide.So I guess that we are left with load time weaving. One disadvantage is that you must manipulate the runtime that your library is deployed in, e.g. by using an agent or custom class loader, see the AspectJ Development Guide for details. If you are working in a Spring environment, you may find Load time weaving in Spring helpful.