March 05, 2005
The New Holy Trinity
No, this isn't a religious post. It's about an approach to developing applications. I'm not the only one advocating this approach, and I'm not the first to write about some of the ideas here. What I want to do in this post though is put all the pieces together in one place and boil the approach down to its essence to give you a simple way of thinking about it. So here it is:
Dependency injection, annotations, and aspects are the three foundations on which the next generation of world-class POJO-based applications should be built.
At the centre of an application designed according to this principle are plain-old Java objects (POJOs). Each POJO should do one thing and one thing only. Each POJO should also know one thing and one thing only - how to do what it is that it does. This is the 'secret' that the the POJO hides from the rest of the application. So for example, a BankAccount POJO should know about the internal state of a bank account, and the operations that it supports. It should not know anything about the way any services it needs are created or deployed. It should not know anything about the authentication or authorization requirements that may be placed on it in a particular deployment scenario - or worse still, anything about the implementation of authenticaton and authorization services in the environment in which it is deployed. It should not know anything about account activity monitoring to detect potentially fradulent use. And so on.
I've written about this notion of a 1:1 mapping between distinct concepts in the design and constructs (types) in the implementation before in "AOP without the buzzwords" (this essay was also updated and expanded to form the introduction for our book, Eclipse AspectJ).
To use some old software engineering terms, what's at the centre of applications designed according to these principles are highly cohesive POJOs. These POJOs are very loosely coupled to the rest of the application. In particular, if you look at the three sides of the triangle in the illustration, you'll see that dependency injection ensures the POJO remains unaware of any of the details of how its dependencies are instantiated and configured. DI shields the POJO from this information. Aspects ensure the POJO remains unaware of any crosscutting concerns in the application (such as the account activity monitoring concern in the example above). Aspects are what enable the POJO to focus on doing just one thing and to do it well. Annotations provide additional metadata about the POJO that facilitate easy integration with external services (such as transaction services). They ensure that the POJO remains unaware of any of the details of such services. Annotations coupled with aspects can do much more than this though - they also support the creation of domain specific abstractions that easily bring back to the programmer Abelman and Sussman's often missing third tool in the war against software complexity: the creation of "little languages" (see my post "A Beautiful Language").
This leads me to a really important point about this trinity of DI, aspects, and annotations. The three techniques are mutually self-reinforcing, the whole is much greater than the sum of the parts.
Dependency injection can be used to configure aspects. This
is important since when you're building an application using
the 1:1 principle, some of the POJOs in the centre of the
triangle will themselves be aspects (in a language like
AspectJ, this is perfectly natural). Anecdotal evidence from a
number of projects across a number of organizations suggests
that maybe about 10% of your "POJOs" will be
aspects. Dependency injection can also be guided by
annotations. You see an example of this in the EJB 3.0
specification (you may recall my own thoughts on the
suitability of the chosen annotation name in that spec "What's
wrong with @Inject?", but that's not material to this
discussion). Completing the circle, aspects can also be used
to implement dependency injection scenarios. In general you're
best leaving this job to an IoC container, but if you have DI
requirements that go beyond the wiring of beans as they are
instantiated by the container, aspects are a great
solution. To give a couple of examples, in the Eclipse AspectJ book we use aspects to implement a
1:1 design for POJO persistence using Hibernate. We use
dependency injection on a per-request basis to inject
Session object that DAOs use for
communicating with Hibernate. A second example is the use of
on POJOs. It's easy to write an aspect (configured by DI to be
BeanFactoryAware in Spring terms) that uses
Spring to autowire any instance of an @SpringConfigured object
when it is instantiated. This lets you separate instantiation
and configuration (normally an IoC container like Spring does
both for you) so that you can place instantiation under
programmatic control, but still get configuration via DI.
Let's look at another really important way in which these techniques work together. Annotations on their own give you a way of specifying additional metadata for program elements. This is useful when you have tools or services that can understand those annotations and do something with them. Annotations coupled with aspects is a whole other ball game altogether. Aspects let you use annotations to easily implement declarative domain-specific languages (DSLs) as part of your application (or aspect library). This is a really big deal. Aspects can do this since they let you associate behaviour with join points relating to annotated elements. To give an example I've written about recently ("Making Concurrency a Little Bit Easier"), you can easily write a set of annotations that form a declarative DSL for concurrent applications (the domain of this DSL is concurrency). Another example I'll write about in my next post: after attending Gregor Hohpe's talk at TSS yesterday on event-driven architectures I quickly implemented an annotatation-based DSL ("@DSL") for simple event-driven applications. It took me less than an hour. That's pretty powerful (you can judge for yourself when you read the post). Annotations plus aspects let you write these @DSLs.
When you're using @DSLs like this in the construction of your application, you're going to want an aspect language that has rich support for join point matching based on annotations. There's a surprising amount to this, and you can see how AspectJ 5 handles it by reading the "Annotations" chapter of the AspectJ 5 Developer's Notebook.
There's another way in which the combination of aspects and annotations are self-reinforcing too. If you are using an annotation-driven tool or service for a particular deployment of your application, you may need to annotate your POJOs with annotations specific to that tool or service. If you don't want to tie your POJOs to that annotation set (see "When is a POJO not a POJO?") you can use the AspectJ 5 support for "declare annotation" to keep the annotations separate and maintain a loose coupling. You might also want to use declare annotation if a particular tool or service requires you to spread information you consider to be configuration information around your source code (in the form of annotations). Here an aspect using declare annotation can at least get that all back in one place for you.
If this all sounds a bit circular (DI of aspects, aspects to perform DI, annotations to control aspect behaviour, aspects to declare annotations,...) that's because it's meant too. The sides of the triangle all support each other and can be used together in many different ways to meet the needs of your particular situation. The whole is much greater than the sum of the parts.
In summary, I believe that dependency injection, annotations, and aspects are the three foundations on which the next generation of world-class POJO-based applications should be built. At the heart of such applications are POJOs that do one thing only and do it well. The triangle illustration provides a simple visual mnemonic to help you remember the principles. An application constructed according to these principles could be said to follow a "triangle design". You can even easily communicate your design intentions in good old ascii:
- 1:1 principle
- A way of structuring a software system such that there is a 1:1 correspondence between concepts in the design and implementation constructs.
- A declarative domain-specific language implemented using a combination of annotations and aspects.
- Triangle design
- An approach to structuring a software system in which POJOs written according to the 1:1 principle are supported by dependency injection, annotations, and aspects.
- Dependency injection
- Eclipse AspectJ book
- AOP without the buzzwords (the 1:1 principle)
- Using little languages to tackle complexity
- What's wrong with @Inject?
- Using AspectJ with Spring
- Making Concurrency a Little Bit Easier
- Event-driven architecture
- AspectJ 5 Developer's Notebook
- When is a POJO not a POJO?
- EJB 3.0
As with any post on this site, this post represents my own personal opinions and not necessarily those of my employer.
Posted by adrian at March 5, 2005 04:04 PM [permalink]
Wow! Great summary of what I've been trying to do for a long time.
I've actually built a system according to these design principles (at http://www.lecando.com), it is truly a pleasure.
Posted by: Jon Tirsen at March 6, 2005 04:01 AM
I have been working on an Aspect animated @DSL with the AspectJ 5 beta during the last two months, and I think same as you do, this is a really big big thing. It is so elegant and powerful (the @declare annotation for decoupling is superb for hibernate and others) that I'm surprised it isn't receiving more attention. Probably it will, and if it doesn't, I plan to capitalize on it ;)
However, Java 5 annotations do have a few quirks here and there which often stand on the way ( see http://beust.com/weblog/archives/000250.html).
And also it yet needs to be proved if aspects can handle animation when your @DSL grows beyond a toy language. Java reflection API ain't the means at all to navigate your domain metadata when things get serious.
Posted by: Pepe Iborra at March 6, 2005 11:35 AM
We will have to find a different name for "POJOs" because Sun is Hijacking its definition: http://www.people4objects.org/Klaus/2005/02/pojos-considered-sexy.html
Posted by: Klaus Wuestefeld at March 11, 2005 05:08 PM
I am wordless! I've been trying to sort out in my mind some thoughts similar to the ones exposed here, but I could never reach closure before … AOP can be seen as the next paradigm shift since OOP, however the idea of creating DSLs using a combination of aspects, annotations and enhanced access to low level program metadata (reflection substitute supported at the very core of the language design) goes beyond the paradigm shift from structured programming to OOP. This new way of creating software puts us in a position where we can start tackling the ever growing complexity of the problems we are expected to solve using computer programming.
Thanks. I can't stop from thinking of our God while I read in your title and article: "Holy Trinity". It reminds me that He loves each one of us beyond anybody's wildest dreams, and it reminds me of His invitation to all of us to be with Him: the Father, the Son and the Holy Spirit, for ever.
I know that yours is a technical article and it helps me understand software design more deeply. Thanks.
hi, i found a site with very nice [url=http://www.planetnana.co.il/jewishgifts/]jewish gifts[/url]
Posted by: firebunny at December 17, 2006 08:47 AM
Thanks for the great post looking for more quality post.
Posted by: ashwini at February 11, 2010 07:43 AM
Thanks for the great post looking for more quality post.
Posted by: ashwini at February 11, 2010 07:44 AM
Post a comment
Thanks for signing in, . Now you can comment. (sign out)(If you haven't left a comment here before, you may need to be approved by the site owner before your comment will appear. Until then, it won't appear on the entry. Thanks for waiting.)