The goal of modeling domain concepts through objects set by OOP has for a long time been handled in insufficient ways. What is the fundamental problem with how we have tried to do this so far? Is there a better way to deal with it? In this article we introduce the concept of Composite Oriented Programming, and show how it avoids the issues with OOP and reignites the hope of being able to compose domain models with reusable pieces.
The problem
Who am I? I am many things. Sometimes I'm a software developer, creating new software. Sometimes I'm a software developer who makes presentations on Java-related topics. But sometimes I'm something completely different, such as a customer to a bank, or an alumni at a university. I am many things, one for each context. Each of these contexts require me to use a different interface to interact with it, with operations that are specific to the context. In all these contexts, with all these different interfaces, I am however one and the same object. There's not one me at the bank, and another writing software.
The typical solution
And yet, if I was to be modeled in software that is probably what would happen. Using object-oriented programming, someone would model me as a Developer class. But that class would have no idea how to go to an alumni get-together, because it has no concept of partying. So I would effectively have to be modeled by several classes, and then one would have to either have several instantiated objects with the having to implement all of these in one single class we instead use the notion of mixins to handle the implementation.
Composites
A mixin is implemented as a plain Java class, that typically implements a particular interface to be exposed by the Composite that it is a part of. The way we then declare a Composite is by creating a Java interface that through annotations declare what mixins to use, and by using the "extends" keyword describe what domain interfaces should be exposed. This gives us a central point that deterministically defines the structure and behaviour of the Composite.
With COP we suggest that while it is a good idea to keep cross-cutting concerns in separate implementation classes, the description of how these should be put together, or composed, is something that should be done centrally, in Java interfaces. In order to avoid duplication of such descriptions we use the "extends" keyword to reuse declarations in the extended interfaces. By modifying an extended interface such modifications will then automatically change the Composite interface declarations that extended it, so that we don't have to modify each of them individually.
With this approach we believe that we get the best of both worlds: the separation of concerns in separate implementation classes, each dealing only with one specific task, and a centralized and deterministic declaration of what the final Composite should look like, thereby putting the developer of the Composite in charge of what should go into the definition of it and what should not be in there.
Code sample
So how would this work in practice? Well, let's take me as an example. If I was a Composite I could be described like this:
@Mixins({DeveloperMixin.class, SpeakerMixin.class, AlumniMixin.class}) public interface HumanComposite extends Developer, Speaker, Alumni, Composite {}
The extended interfaces would contain the actual methods to be invoked, and it is up to the Qi4j runtime to construct a Composite instance that can route invocations from clients to specific mixin instances. But it is important to note that from the clients perspective this is just a regular Java object, even though it perhaps has more interfaces than your usual domain object would have if it was implemented using regular OOP. And the domain interfaces, such as Developer, are regular interfaces with nothing specific to Qi4j, and the implementations themselves are also simply Java classes that implement the interface. The identity of the object, however, is defined by the Composite instance rather than the individual mixin instances, that solves the identity crisis: a reference to "me", as an object, can be sent around the system and casted to the interface that is useful in that particular context. If more domains and contexts are introduced, then the Composite can be extended to handle them too.
If we want to create another Composite that also uses the Alumni interface and implementation, we can do so by having that interface extend Alumni as well, and declare the same mixin to be used. The usual problems with multiple inheritance and reuse of base classes is thereby avoided.
Structural issues
Software is often designed on paper using modules and layers. We are all very familiar with diagrams like the following.
It contains Modules which are part of Layers, that are stacked on top of each other. Let's call it the Layered Modules Metaphor (LMM). It is used to communicate an overview of the entire application, without getting the audience bogged down with too many details. By rigorously following the LMM of the project, we benefit from fewer system-wide bugs, lower long-term maintenance cost and often a more flexible system that can react to change. Most projects use the LMM to explain how the application is structured, many try to follow it, but very few projects enforce it, and I think we all have seen horror cases where for instance classes in the infrastructure layer uses classes in the web layer.
Providing structure
Qi4j has taken the bold route to provide explicit support for the LMM, to discipline the developers on the team. The Qi4j application structure is a small set of rules:
- Structure
- All Structure is declared statically at boot time.
- All Composite instances belong to a Module.
- All Services belong to a Module.
- All Modules belong to a Layer.
- All Layers are on top of zero, one or many other Layers (but not cyclic).
- All Layers form the Application.
- Access
- Modules can access all other Modules in the same Layer.
- Layers can access the Layers directly below it (not transitive).
- Visibility
- Composite instances are by default only visible within the Module.
- Composite instances can be made visible within the Layer, and between Layers.
These sound a lot more complicated than it is in reality. Essentially, Composite instances are private within the Module where they were created, unless explicitly made public either outside the Module, or outside the Layer. This is similar to how we set "public" or "private" modifiers on classes and methods in regular Java programs.
The structure is not optional, but convenience methods exist to establish common application structures, including a single module in a single layer.
Using structure
Domain code does not need to know about the application structure, it is just "there", but it shows up in the form of the @Structure annotation. Below is an example of this.
The CompositeBuilderFactory will be injected to the mixin upon creation. It will only allow the code to instantiate Composites which are visible from its point in the structure.
Another common case where Structure comes into play is when a Service is looked up. If one and only one Service of the requested type is found in the same Module, then no additional assembly will be required. Using Services becomes extremely simple.
For instance, if a GenericInventory service is declared in the Bread Module, each inventory service instance will be bound by its proximity to its respective client.
Establishing Structure
Qi4j applications need to be bootstrapped by application code. The simplest start-up possible looks like the following.
Another way to write this is to use the SingletonAssembler:
The SingletonAssembler is a convenience class that creates a Qi4j application as a single module in a single layer.
It is also possible to pass an Assembly[][][] to the newApplication() method, which will create an application context with a "pancake" layering, i.e. each layer is on top of one layer and below one layer (except the first and last one). For instance
will give the following structure:
And finally, for arbitrarily complex application structures, one needs to pass an ApplicationAssembly instance to the newApplication() method. The ApplicationAssembly is used by iteratively building up the LayerAssemblies and from them iteratively building up the ModuleAssembly. Here is an example.
This produces the following structure.
Benefits of Structure
The two main benefits from explicit coding of applications are as follows.
- Resolution by Proximity.
- Architectural Enforcement.
What this means is that composites that are nearby are easily accessed, and that nearby composites will have a higher priority, and composites private to a module or a layer is not accessible from the outside. Since services are composites, the service resolution becomes more implicit and requires a lot less assembly configuration.
Another interesting side-effect of the Structure concept in Qi4j is that every application has a static structural composition, which can be extracted and visualized by tools, instead of maintained separately. This enables architects, designers and team leads to track whether developers follow the architecture, or whether they are trying to circumvent it.
Conclusions
In this article we have briefly explored the possibilities of Composite Oriented Programming, as they are implemented by Qi4j on the Java platform. We have looked at how Composites, which implement objects in the traditional OOP sense, provide a way to achieve better separation of concerns, which leads to better quality and more reusable code. We have also explored the idea of explicitly modeling the structure of an application, which is typically only defined on paper and not in code. By doing this we can more easily handle architecture enforcement and resolution of dependencies between services. This helps us create larger systems which do not crumble under their own weight as more and more components and services are introduced.
Finally it is important to stress that most of the concepts in COP and Qi4j are not new. What we have done is to identify good ideas and patterns from previous programming practices and frameworks, and have distilled those that we believe are helpful to developers in both writing software and keeping it understandable and maintainable. Adapting age-old metaphors in new contexts is important, whether it has to do with software or life in general.
About the Author
Rickard Öberg has worked on several OpenSource projects that involve J2EE development, such as JBoss, XDoclet and WebWork. He has also been the principal architect of the SiteVision CMS/portal platform, where he used AOP as the foundation. Now he works for Jayway, and is interested in how to develop domain-oriented software that is well adapted for the new wave of Internet-centered applications.
About JayView
JayView is Jayway's magazine. Jayway is a company focusing on Java development. The magazine is where we put our passion for Java on cellulose.
We do not write about ourselves - or for that matter - how beautiful our customers are.
We write about our passion for coding, for creating great software - basically just all the things we enjoy. For more JayView visit www.jayway.com/jayview.