In systems architecture, there are rarely any right answers – mostly just trade offs between one solution or another. In such cases it helps to bear in mind some fundamental principles as a guideline. One principle I often use is cost vs. benefit. Another useful principle is to minimize coupling between systems. Coupling is pervasive and leads to a kind of inertia in enterprise systems. Newton discovered that inertia prevents change and if there is one thing that enterprises struggle most with, it’s change.
Coupling as Inertia
November 20th, 2010 — soa
Dimensions of Coupling
May 12th, 2009 — architecture, distributed-computing
Coupling is one of the most fundamental measures of “quality” for an information system. The concepts of coupling and cohesion appear in software design best practices for at least a couple of decades. And these concepts are also vital to the development of distributed systems. As core as the concept of coupling is, it is difficult to find a real definition in the distributed systems context. Coupling is like obscenity – we can’t define it, but we know it when we see it.
Which is why I was pleased to see Ian Robinson’s post which presented coupling as lying on two dimensions – temporal and behavioural and even put in place some characteristics which helps you put a rough measure on the degree of coupling. Coincidently, I had drafted my own version of this some time ago, but it had never made it to publication.
Like Ian, I was trying to quantify coupling so that we can understand what constitutes a tightly or a loosely coupled system and we can have some approach to measure it and therefore have a method to decide between design trade-offs in satisfying the various requirements of our distributed systems. While Ian presents a conceptually clean two-dimensional picture, I felt the true story involves multiple interacting dimensions.
While I was researching this, I happened to find a book extract which covers what I wanted to say and more. The full extract is well worth reading, but is summarised in the following table:
Level | Tight Coupling | Loose Coupling |
Physical coupling | Direct physical link required | Physical intermediary |
Communication style | Synchronous | Asynchronous |
Type system | Strong type system (e.g., interface semantics) | Weak type system (e.g., payload semantics) |
Interaction pattern | OO-style navigation of complex object trees | Data-centric, self-contained messages |
Control of process logic | Central control of process logic | Distributed logic components |
Service discovery and binding | Statically bound services | Dynamically bound services |
Platform dependencies | Strong OS and programming language dependencies | OS- and programming language independent |
Here we have no less than seven dimensions to the coupling equation.
The final paragraph of this article highlights the costs of loose-coupling (and only some of the benefits).
However, in most cases, the increased flexibility achieved through loose coupling comes at a price, due to the increased complexity of the system. Additional efforts for development and higher skills are required to apply the more sophisticated concepts of loosely coupled systems. Furthermore, costly products such as queuing systems are required. However, loose coupling will pay off in the long term if the coupled systems must be rearranged quite frequently.
I think this understates the benefit. “Rearranged frequently” seems to only cover design-changes. But it should also cover “runtime rearrangement” such as partitioning across redundant components for the purpose of load-balancing and fault-tolerance. In such cases, “loose-coupling” provides significant value in higher uptime and scalability of distributed systems.
Update May 14, 2009: Richard Veryard has pointed me to his paper “Component Based Service Engineering” (subscription required) which discusses an even wider range of coupling beyond the technical layers into Process, Organizational and Business layers. The CBDi Wiki has a table summarizing all the coupling dimensions identified in Richard’s paper.
One section of the paper struck a chord with me:
How can we have loose coupling and hard-wiring at the same time? The answer comes as soon as we recognize that coupling is multidimensional or multilayered. My head is connected (coupled) to the rest of my body in several different ways. Even if I could introduce some technology to decouple the nervous system, that doesn’t allow me to remove my head….With Web Services, SOAP simply removes one set of the hard-wired connections. Other forms of coupling remain.
This was written in 2003 and proves quite prescient in that many SOA projects in the interim have failed to achieve their goals by simply adopting out-of-the-box “web services” which only address one or two of the many dimensions of coupling.
Ian Robinson on Coupling
April 28th, 2009 — architecture, soa
In my opinion, coupling is the most fundamental attribute of a system architecture and tight coupling is probably the most common architectural problem I see in distributed systems. The manner in which system components interact can be a chief determinant of the scalability and reliability of the final system.
So I really like Ian Robinson’s post on Temporal and Behavioural Coupling where he uses two coupling dimensions and the inevitable magic quadrant to classify systems based on their degree of temporal and behavioural coupling.
See Ian’s post for the slick professional graphics, but to summarise – event-oriented systems with low coupling occupy the “virtuous” third quadrant of the matrix. Conversely the brittle “3-tier” applications that many of us struggle with, occupy the “evil” first quadrant where coupling in both dimensions is high.
However I’m a little miffed to see no mention of my favourite “document-oriented message” in Ian’s diagram. As Bill Poole writes; document messages have lower behavioural coupling than command messages, but more than event messages. So would you put document-oriented messages near the middle top of the matrix between command-oriented and event-oriented messages? Unfortunately that would break the symmetry. But it also highlights another problem.
Any type of message – document, command or event-oriented could temporally be tightly or loosely coupled. Temporal coupling is more a property of the message transport than of the message type. So I suggest that the two coupling dimensions are characterised as follows:
- Temporal coupling – characterised by message transport from RPC (tight coupling) through to MOM (loose coupling).
- Behavioural coupling – characterised by the message type from event-oriented (tight) through document-oriented to event-oriented (loose).
It so happens that distributed 3-tier systems generally employ both command-oriented messages and RPC transports – hence making them inherently “evil”. Whereas events (being asynchronous) are naturally virtuous by typically being carried over MOM transports (it’s difficult to request an event notification).
Between heaven and hell, it is in the murky mortal realms of SOA where we need to be constantly mindful of the interactions between message type and transport – lest our system ends up in limbo.
The Architectural Role of Messaging
September 20th, 2008 — distributed-computing
JMS has brought messaging more into the mainstream which is a good thing. But just like any new technology there is the danger that the first implementations will reflect older paradigms. I remember when I made the move from FORTRAN to C, for a while I wrote a lot of FORTRAN programs in the C language syntax until I got more familiar with C features and idioms. The same goes for my more recent ventures into Ruby with many years of Java thinking under my belt.
Coming back to JMS, I find when I review distributed applications that have been designed by people with a strong client-server or web background I see a lot of rpc message semantics. While rpc (or synchronous request/reply) has it’s place, this is not always the best approach. A common mistake is to regard messaging as simply a way to get messages from point A to point B…treating JMS as a simple transport such as a TCP socket or HTTP.
Messaging originated as the concept of a distributed queue. Most programmers are familiar with queues from GUI frameworks where communications between widgets are mediated via an event queue. The event queue supports a number of functions such as decoupling widgets from each other…allowing each widget to do what it needs to in it’s own time, and supporting event driven interactions between widgets. The event queue along with multi threading is key to giving user interfaces the responsiveness and robustness that you expect. In this way queueing provides more than just a communications mechanism but is key to the architecture of a GUI framework.
The same is true of distributed messaging systems. In their original conception distributed queues do more than just provide a way for data to pass from one system to another, they provide an important element of isolation.
A fundamental difficulty in building distributed systems is that the different components have different performance characteristics. In addition the uptime of your total system is the product of the uptime of individual components. To ensure maximum uptime you want your components to be independent of each other and, in the event of failure you want to be able to restart from where you left off. This is where message queues work really well. Component A puts a message onto a queue and doesn’t care if or when component B takes that message off the queue. This is known as the fire and forget message pattern and it provides the best isolation between your system components.
If instead we make Component A wait for an acknowledgment from Component B before it proceeds then we are building a tight coupling into the system. Any performance difficulties or failure experienced by Component B could spread back to Component A and thence to other components up the chain.
So the role of messaging in distributed systems goes beyond just getting a message from point A to point B. It also acts as a kind of expansion joint for your system allowing individual components to vary in their performance characteristics – or even fail totally for short periods – without breaking adjacent components.
Without these messaging expansion joints, your system is tightly coupled and prone to system wide failure originating from a single component. Messaging – using the fire and forget pattern – allows these issues to be locally absorbed and managed within normal system operations.