Tuesday, August 16, 2011
The Package as Contract
A software contract is a collaboration scenario; the contract designer is the choreographer allowing the provider and consumer safely do their dance. In this choreography, you sometimes need an interface to describe the role of the consumer of the contract; we often call those interfaces listeners. Other times, the onus is on the provider and then we frequently revert to names like managers or servers. Sometimes we need real classes to provide event objects, exceptions, permissions, or small helper classes to make the overall contract easier to use. Sometimes, though rarely, there is even a need for private classes to implement parts of the contract in the contract. It should be clear that a contract is much more than just an interface.
Contracts change over time and this raises the issue of versioning. Most of the time when we talk about versioning we only talk about the provider and the consumer versions. However, the whole idea of contract based the design is that the consumer does not depend on the provider, it only depends on the contract. So in reality, the most important version is not the provider's version, it is the contract version! In most build systems this is very awkward as it forces a contract to be specified by an artifact, the only thing that is versioned. Since the best contracts are well defined, highly cohesive contracts this creates a proliferation of artifacts, containing only a few packages. This is cumbersome so people tend to aggregate them and version them together. Often even bunching them up with a provider! However, a side effect of aggregating contracts is that every contract evolves at the speed of the fastest changing contract, causing many unnecessary version update ripples through your system. If you include a provider, the speed even increases more.
By far the best solution I know how to handle contracts is to take the package in Java very serious. A package is really a module, it has encapsulation (classes, interfaces, and resources) and it can control accessibility. It provides all we need to define a contract. The only missing thing in plain Java is that packages are unfortunately not versioned.
OSGi was made for design by contract from the start and that is why we've taken packages so serious. In OSGi packages are versioned. This is a lot easier that versioning artifacts, although many believe the opposite. The consequences of a change in a contract package is a lot easier to understand than a change in an aggregation of thousands of classes. In the OSGi we have a lot of experience with package versioning and most packages are still at surprisingly low numbers. We actually maintain the version of a package as resource in the package or an annotation on the package.
Designing by contract also explains why OSGi bundles import and export packages and not require aggregations of thousands of classes. The intention is even that shared packages should only be contract packages. In an ideal OSGi system there is no need of sharing implementation classes.
With this experience behind our belts it is therefore hard to see how Jigsaw will provide a model that seem to ignore these lessons of contract based design in a modular world.