Tuesday, August 16, 2011

The Package as Contract

Contract based design is, from my perspective, one of the best insights our industry had for the past 50 years. With contract based design you separate the providers of the contract and the consumers of the contract. In Java, there is a sometimes tendency to see these contracts as just an interface. The provider is the implementer and the consumer is the client of the interface. I think this is misguided because an interface is way too limited for real life software contracts.

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.

Peter Kriens

1 comment:

  1. My favorite bit: "In an ideal OSGi system there is no need of sharing implementation classes."

    Unfortunately, this is rather challenging for developers to grasp, and even harder for them to abide by.

    Agreed, completely hiding all implementation classes is often unavoidable, that is, unless you use technologies such as DS and the OSGi service registry, that allows providers and consumers to collaborate anonymously.

    But without such technologies it's easy to see how developers create modules that are tightly coupled, that also exhibit low cohesion.

    It is my opinion that loose module coupling and high module cohesion are misunderstood, under-rated, and mostly ignored by developers, contributing to the lack of modularity in OSGi systems today.

    Nice blog post, Peter. Thanks.