Вы находитесь на странице: 1из 3

Loose Coupling – What does it really mean?

Dan Rosanova explains intricacies of


coupling in services and code
Every developer of the last decade knows that coupling in software is bad. It is an anti-
pattern, an example of what not to do. Yet if you ask developers what loose coupling is
they mostly give examples, not a definition.
I myself couldn’t create a clear definition until I first wrote on the subject, and as you’ll
see it’s still not that clear. Part of what makes coupling so difficult to understand or spot
in your own projects is that there are
actually many different types of coupling. The following are all examples of coupling:

• Contract/Interface

• Transport

• Location

• Time

• Platform (should be less of an issue in SOA, but it’s still there)


Some of these are obvious: transport and location represent clear concepts that Services
are designed to help alleviate. Others are a little more complex. Consider contract
coupling. Contracts can couple in obvious
ways, like types, and less obvious ways. Recall that one of the principles of a good
service that it have an explicit contract. XSD does a great job making contracts explicit
and enforceable: it allows for strong type
definitions, complex types, and type validation. One thing XSD cannot address, however,
is implicit coupling. I often see two distinct forms of implicit coupling: untyped messages
and implicitly ordered contracts.
Untyped messages This is one of my favorites as there is almost a good reason behind it:
namely flexibility. Generally it is believed that by using an untyped message (string or
xs:any) a service interface can be
changed at will without the normal planning (or pain) required. This anti-pattern service
will often expose a method called Execute or something similar and take a string
parameter of the XML payload or just xs:any.
This sounds like it might actually provide easy changeability and low maintenance, but
there are two fundamental flaws with untyped messages: first consumers have no way to
know what is legal to send.
They have to ask for example payloads or a separate schema file outside of the service
definition. This means the service is not well encapsulated. Worse they only know if their
message is correct once they actually
send it. The second issue is that untyped messages accomplish nothing. Just because you
can change this vague implicit contract without the cooperation of your consumers does
not mean they can now use this service. Remember, they’ll be sending what they thought
was the correct message before this contract update. Now they will just be sending the
incorrect, old format and not know why it doesn’t work the way they expect.
I’ve seen some reputable software companies take this approach, and I’ve never seen it
work out well. There is a difference between extension points in a schema and untyped
messaging.
As my friend Phil Boardman pointed out ‘Untyped messaging is basically an angle
bracket delimited text file’ (Phil does admit he didn’t think of this, but I heard it from
him).
Implicitly ordered operations This is a much more subtle, and perhaps more dangerous,
type of coupling. This is where implicit rules or restrictions work their way into a service.
Imagine a service with operations like these:

• Login

• Add Item to Basket

• Validate Order

• Place Order
This should look funny to begin with for a few reasons. First of all this looks a lot more
like an API than a service, and second you can see there is probably some sort of order
expected for these operations,
but it is not clearly defined. You would need a separate read me document to tell you that
you must first call the Login operation, then Add Item to Basket, then Validate Order
before calling Place Order.
WS-Policy is one way to solve the order of operations, but it would only mask the
problem (mostly because it is not widely adopted or understood).
This example service also violates other principles: Autonomy and Statelessness (these
will be covered in a later post). By requiring multiple separate operations to be sent in a
specific order this service implicitly
contains state within it (if it didn’t it wouldn’t know about your shopping cart or the items
in it) and it forces consumers to understand its internal working (the order of operations).
These are bad signs as changes
to the service will almost certainly impact the consumer. Let’s suppose your service now
needed to calculate tax or shipping. This type of service would have no real place for
them.
This service should really exist as one operation: Place Order. In the implementation of
this service the validation and login should all take place together in one unit of work or
transaction script.
The continuum of coupling
Service design is a series of tradeoffs; the impacts of which are not always immediately
clear and thus coupling is really a continuum and where your services fall on this
continuum will vary with every implementation
based upon these tradeoffs. For instance the goal of flexibility is in direct opposition with
the goals of validation and control. This is why service design deserves careful attention
before you begin implementation.
This is the essence of contract-first development, and it is a good idea both in code and in
services. Test Driven Development really shines here because you will get to know what
your services are like before you are stuck with their legacy implementations.
Definition of Loose Coupling
When I alluded to that definition of loose coupling I would probably break it down like
this: Something is loosely coupled if its interactions are separated by abstractions of type,
transport, platform, and state.
So how can you tell if a service (or code for that matter) is loosely coupled? This part is
strangely simple. If it is difficult to test a service, class, or method then it is probably not
loosely coupled. Inversely, code
that can be easily tested without major setup and teardown is loosely coupled. To be clear
when I say test, I mean effective, completely automated tests. It’s very easy to make tests
that are almost useless without
realizing it. A test suite for even a single service should cover all the major expected
scenarios for valid and invalid service invocation.
This is one of those “Code Smells” Martin Fowler writes about in Refactoring and you
should be aware of it. When you start to realize your setup and teardowns are very large
and cumbersome your code is
slipping into tight coupling. This is the same for services and code in general (which is
normally service implementations). This is probably the last warning sign that you’re
about to have major maintenance problems;
unless you don’t have adequate tests, in which case you’ll end up with major production
problems.

Вам также может понравиться