Let's start with some terminology and definitions. A pointcut language can be seen as a Domain Specific Language (DSL) for defining pointcuts. A pointcut is defined as the set of join points - a pointcut picks out join points, in which a jointpoint is defined as a well-defined point in the program flow.
Sounds complicated? It's actually pretty simple.
Let's try a little example. You have an instance of a class Foo and an instance of a class Bar. The class Bar has a method with the signature void setFoo(Foo foo) and the class Foo has some other method that is invoking this method in Bar. E.g. something like this:
Then the point where setFoo(..) is invoked from the class Foo is a well defined point, e.g. a join point.
If we now want to pick out this point in order to do something with it (apply an advice or interceptor to it or something else), then we can create a pointcut using a pointcut language. In this particular case we could create a pointcut defined like this:
Now we have created a pointcut (AspectJ style), since we have a construct that picks out join points. The only oddity here is that it only picks out one single join point.
Since just working with one join point at time is fairly useless, we need an expressive way of picking out and managing 'sets' of join points.
Luckily, most pointcut languages supports using patterns to write more expressive, generic or specific, pointcut expressions.
For example, you can use patterns to pick out the call to any method that starts with set in Bar using this pointcut:
In this pointcut we do not care about the parameters to the method, the return type or its name more than it should start with set.
Generally, pointcut languages have two families of patterns (mini-languages):
Signature patterns - matches signatures (method, field etc.)
Type patterns - matches types
(For a more detailed description of the patterns and semantics that the AspectJ pointcut language supports, see the AspectJ documentation.)
The problems with using signature patterns in pointcuts
A pointcut can be seen as an implicit contract between the target code and the artifact that is using the pointcut (could be an aspect or an interceptor).
One problem with using patterns like this is that we are basing the implicit contract on implementation details, details are likely to change during the lifetime of the application. This is becoming an even bigger problem with the popularity of agile software development methodologies (like XP, Scrum, TDD etc.), with a high focus on refactoring and responsiveness to customer ever-changing requirements.
Another problem is that the use of patterns effectively removes all strong typing (can be helped with good tools like AJDT, but that will tie you to a specific working environment).
As a rule of thumb:
Your application's implementation details will change and you should therefore, as much as possible try to avoid basing implicit contracts, e.g. your pointcuts, upon them.
So what can we do about it?
Metadata to the rescue?
When I write metadata I mean data about the target artifact, data that describes the characteristics of the artifact.
Here is a decent definition of metadata:
Metadata are structured, encoded data that describe characteristics of information-bearing entities to aid in the identification, discovery, assessment, and management of the described entities.
Note: when I write metadata I do not mean any specific kind of metadata, it can be declared in code, text file, XML, some meta model etc. But to be more specific, let's look at Java 5 annotations.
Since metadata is raising the abstraction level over implementation details, but still captures its characteristics, it seems to be very suitable for helping us addres the issues with signature based patterns discussed above.
So, all this sounds promising, but as we will see later, you have to be aware of that not all metadata will help here and you have to define it with care.
Ramnivas Laddad has written an interesting article on metadata and pointcuts, where he views metadata as being multi-dimensional signatures. For example (from the article), a method with the following signature suffers from signature-tangling:
This method's signature is tangled since it is trying to capture all its roles in it's signature, e.g. name. In this case the use of metadata can help to untangle this signature by defining the method like this:
This is definitely an improvement and the benefits are clear; more stable contract, more readable code, opens up for potential use of the metadata by tools etc. But the problem is that it doesn't really raise the abstraction level, we still have strong implicit coupling, and the annotations suffers from the @AdviseMeHereCode Smell. I think the main problem is that we are still relying on implementation details, since we have merely restructured the signature.
He then continues with a discussion on the increased coupling, and suggests using more generic annotations that can be consumed by many different tools. For example that you should prefer using annotations like @ReadOperation rather than @ReadLock etc., which I think is an example of a step in the right direction.
The way I see it:
If an artifact F is annotated with an annotation A, then A should only describe characteristics (behaviour, role, responsibilities or properties) that F had before A was added to F.
In other words, you should try to avoid using annotations that implies explicit changes of the characteristics of the annotated artifact.
This raises the questions like, if for example the use of an @Transactional annotation (or @TransactionAttribute in EJB 3) is a bad idea? Well, yes (and no).
Generally speaking I do consider any annotation that have an Advise-Me-With-Feature-X kind of flavor, a code smell. However, I do think there is a place for these kind of annotations in well-defined and constrained environments, such as containers and frameworks - as well as when you think it works for you, stay pragmatic... :-).
I do believe that metadata still can help us, but the questions are: What is the proper abstraction level? How to make it work in reality?
Metadata and Ubiquitous Languages
I am a big fan of Domain-Driven Design (and Eric Evans book with the same name). One of the key building blocks in Domain-Driven Design is the Ubiquitous Language, and I have found that basing metadata abstractions on a Ubiquitous Language can get us a long way. More about how and why in a minute, but first, some people might be wondering what a Ubiquitous Languages is? Eric Evans defines it like this:
Definition: A language structured around the domain model and used by all team members to connect all the activities of the team with the software.
A project faces serious problems when its language is fractured. Domain experts use their jargon while technical team members have their own language tuned for discussing the domain in terms of design.
The terminology of day-to-day discussions is disconnected from the terminology embedded in the code (ultimately the most important product of the software project). And even the same person uses different language in speech and in writing, so that the most incisive impression of the domain often emerges in a transient form that is never captured in the code or even in writing.
Translation blunts communication and makes knowledge crunching anemic.
Yet none of these dialects can be a common language because none serves all needs.
Use the model as a backbone of a language. Commit the team to exercising that language relentlessly in all communication within the team and in the code. Use the same language in diagrams, writing and especially speech.
Iron out difficulties by experimenting with alternative expressions, which reflect alternative models. Then refactor the code, renaming classes, methods and modules to conform to the new model. Resolve confusion over terms in conversaion, in just the way we come to agree on the meaning of ordinary words.
The Ubiquitous Languages is not only usable in the domain layer, but can, as Jimmy Nilsson has pointed out, be very useful in the service layer or in DSLs.
I have found that using the Ubiquitous Language is equally important when defining metadata. Metadata should be a first class citizen in the model and therefore has to be part of the Ubiquitous Language. This is something that is very useful by itself, but becomes even more important in the context of pointcut languages.
Bringing it all together: Domain-Driven Pointcut Design
So what is Domain-Driven Pointcut Design (more than a fancy name for a thing that can be seen as common sense, that you can make yet another buzzword-like acronym out of)? Simply a way of designing pointcuts that are anchored in the model and that are more resilient to change.
My proposal is that you should try as much as possible to to avoid the use of patterns and signature based pointcuts. What this means in practice is that you need to use match-all signature patterns (since we still have to define a pattern for signatures in the AspectJ pointcut language), e.g. '* *.*(..)' for method signatures or '* *.*' for field signatures etc. You should then instead try to constrain the pointcut language you are using to three different matching constructs:
Types - only fully qualified names, names that are part of the Ubiquitous Language your team is using.
allows you to use regular Java imports, so you can write
Metadata - only annotations that are part of the Ubiquitous Language your team is using.
call(@AccountStateChange * *.*(..)),
execution(@Idempotent * *.(..)),
or whatever makes sense in your model.
Package patterns - should be used only for narrowing down scope.
etc. This means that
within(com.biz..) is ok, but not
If you manage to implement this correctly, in your team as well as in your code, then you have a high chance of being able to define extremely stable pointcuts that are a natural part of the model that both the developers and the domain experts (customers) can understand and reason about.
Finally, I do not and believe that signature based pointcuts are useless, should be banned or anything. You should of course still use signature based pointcuts when they are the best tool for the job, and when based upon standardized interfaces, classes or annotations then they can be just as stable and communicative.
This has not been a rant about the AspectJ pointcut langauge, which is in fact the most elegant, concise and coherent DSL I have ever worked with. But an attempt of showing a way of using it, in the context of Domain-Driven Design, that I have found useful and that addresses some of the problems with staying in sync with the model and the rest of the code throughout the lifetime of the application.
Update: Added some example pointcuts in the section titled Bringing it all together: Domain-Driven Pointcut Design