Antidoto – the beginning
In the last months I participated in projects using spring and maven2 to build coarse grained software modules.
While splitting a project in smaller pieces is not really a new concept – and with maven it is even easy to accomplish – it still is a major problem to integrate those parts together again.
In my session at the W-JAX 2006 conference I spoke about the lack of architectural guidance when building software based on coarse grained pieces. With the upcoming session at the Spring Day in Germany, I decided to opensource the actual codebase.
Antídoto is the portuguese word for antidote. An antidote is a substance which can counteract a form of poisoning. Antidoto is a java technology mashup providing building blocks for modular architectures. Modern java technologies are component based, but lack modularization capabilities. Here is where the poisoning is happening and where Antidoto jumps in.
Because thats what people say when they see it. “Thats interesting”. Thought it would be fair to call them this way… ;-)
Because it was the beginning of Antidoto. The feedback from colleagues, customers and listeners at the W-JAX conference encouraged me to go ahead and create my first open source project
Form over substance
The earth is populated with shallow and ignorant people. That’s why form will always be more important than substance. You can waste your time complaining about how that should not be the case in a perfect world, or you can snap out of it an follow my advice.
If a document is over two pages long, few people will ever read it. And those who do read it won’t remember it in twenty-four hours. That’s why all your documents should be over two pages long. You don’t want your readers to be influenced by a bunch of facts. You want them to look at your creative use of fonts, your brilliant application of white space, and you inspired graphics. Good formatting leaves the reader with the clear impression that you are a genius and therefore whatever you’re writing about must be a good idea.
The Dilbert Principle,
Goals and background
The goal of this series of articles is to provide an overall architectural guideline for Java (TM) based enterprise software development. Each software project has its own special requirements which must be analyzed by themselves, but many software projects have common requirements:
Reusability of Software Components
The modern development today imposes that software products should not be a monolithic piece of software. Therefore the system should be divided and developed in subsystems. In an assembly process, the deployment artifacts will be generated out of dependent subsystems.
Increased Software Quality
Software quality factors are non functional requirements, among them
- Performance and Scalability
The new architecture should allow to scale the deployed system to achieve the needed performance, without having to rewrite code, transparently to both the developer and the users.
Multiple teams and offshoring
Many projects have a very short development time, so work is accomplished by many teams, on site, off site and off shore. Teams must be able to work together on the same project with minimal impact on efficiency.
Architectural principles applied
This architectural guideline is based on common architectural principles.
The front end consists of one or more web applications, rich clients, integration layer consumers. This layer should only provide view logic (presentation, internationalization, navigation) and no business specific logic.
Any client specific state will be stored in components in this layer.
The business layer provides the business logic used by the presentation layer.
The integration layer provides access to the required resources, among them legacy Corba applications and different relational database resources.
In short, Service-Oriented Architecture describes a perspective of software architecture that defines the use of services to support the requirements. In this special case, some of the subsystems to be defined may expose such remote services to the overall system.
As the system becomes implemented and the number of point to point connection increases, the usage of a enterprise service bus might be evaluated.
One of the major goals of modularization is to divide complex monolithic systems in less complex smaller modules.
A modular design is characterized by
- Functional partitioning into discrete scalable, reusable modules consisting of isolated, self-contained functional elements
- Rigorous use of well defined modular interfaces, including object-oriented descriptions of module functionality
- Ease of change to achieve technology transparency and, to the extent possible, make use of industry standards for key interfaces.
Decomposed modules were renamed to subsystems in this architectural guideline.
A subsystem must have a system wide unique identifying name. Addressing subsystems is explained in detail later.
Each subsystem should hide its internal implementation from external components in the system. The architecture can enforce encapsulation by name conventions (using standard Java packages) and AOP techniques (declaring compilation errors).
Each subsystem “owns??? one package name, preferably similar to the groupdId and artifacId names – subsystem these identifiers are explained later. The architectural prototype uses exactly the groupId and artifactId to define the package names.
Unfortunately the Java package encapsulation does not know hierarchical encapsulation. The usage of more than one package name for one subsystem leads to public internal interfaces. In this case the developer should know that public interfaces in hierarchical sub-packages must not used from external subsystems, they are for internal use only.
This is the main reason why a naming convention for subsystem package names should be defined and known by every developer in the development teams.
Naming the pieces
Very important in an architecture guideline is to define and document the constituting pieces of the software.
It is of central importance for everybody in the development teams to know how to name the composing parts, and to understand the boundaries and size of modules, subsystems, systems and components – if these are the names used in the architectural definitions.
For this series of articles I have given the software product and the composing parts the following names:
Putting pieces together
Decomposing a software in smaller pieces is not new to the software industry.
Divide and conquer has been the slogan for years. The problem is, once you got the pieces, you have to put them together to work as one again. This is where the technologies, as we know them today, fail.
Composing a system out of decomposed subsystems means you have to know subsystem dependencies:
- compile time dependencies
- runtime dependencies
The compile time dependencies arise as long as we have subsystems interacting with other subsystems. Since this is a very common situation, we will have many compile time depencies.
The runtime dependencies arise as we develop against interfaces. Our software components will not instantiate the needed classes by themselves (see dependency injection, inversion of control). There will be a factory responsible for wiring up component beans and their dependencies. The dependencies described here are runtime dependencies.
Solving the jigsaw puzzle
Jigsaw puzzle: puzzle consisting of differently shaped pieces which fit together to form a picture
Here comes a small example, just to emphasize how complicated things can get when you start developing subsystems and managing their dependencies. We will develop two systems (A & B):
- JSF/ Facelets UI
- Business logik bl1 and bl2
- Persistence db1 and db2
- RCP client
- Business logic bl2 and bl3
- Persistence db2 and db3
The next picture is showing the dependencies between the subsystems and the spring libraries, but the dependencies to all other libraries are still missing.
The point here is: it is almost impossible to manage all the dependencies here by hand. Imagine if each subsystem in the picture becomes replaced by at least two modules, one for the interfaces and another for the implementation.
There are different approaches to manage dependencies, among them are the OSGi frameworks providing plugin architectures.
The prototype developed along with this architecture guideline does not use an OSGi framework.
- To manage the compile time dependencies, it uses maven2.
- To manage the runtime dependencies, it uses the bean factories from the spring framework.
To get along with this jigsaw we need an integration server and automatic builds and testing. This is not new to modern and agile Java development. This is called continuous integration.
There are different categories of subsystems. In a very simple approach, we will at least find subsystems containing…
- the technical infrastructure
- the business logic
- the persistence logic
- the presentation clients
Thank you for reading!