Now we are able to define a Service Oriented Architecture (SOA). SOA
is an architectural style whose goal is to achieve loose coupling among
interacting software agents. A service is a unit of work done by a service
provider to achieve desired end results for a service consumer. Both
provider and consumer are roles played by software agents on behalf of
their owners.
This sounds a bit too abstract, but SOA is actually everywhere. Let's
look at an example of SOA which is likely to be found in your living room.
Take a CD for instance. If you want to play it, you put your CD into a CD
player and the player plays it for you. The CD player offers a CD playing
service. Which is nice because you can replace one CD player with
another. You can play the same CD on a portable player or on your
expensive stereo. They both offer the same CD playing service, but the
quality of service is different.
The idea of SOA departs significantly from that of object oriented
programming, which strongly suggests that you should bind data and its
processing together. So, in object oriented programming style, every CD
would come with its own player and they are not supposed to be
separated. This sounds odd, but it's the way we have built many software
systems.
The results of a service are usually the change of state for the
consumer but can also be a change of state for the provider or for
both. After listening to the music played by your CD player, your mood has
changed, say, from "depressed" to "happy". If you want an example that
involves the change of states for both, dining out in a restaurant is a
good one.
The reason that we want someone else to do the work for us is that they
are experts. Consuming a service is usually cheaper and more effective
than doing the work ourselves. Most of us are smart enough to realize
that we are not smart enough to be expert in everything. The same rule
applies to building software systems. We call it "separation of
concerns", and it is regarded as a principle of software engineering.
How does SOA achieve loose coupling among interacting software agents?
It does so by employing two architectural constraints:
-
A small set of simple and ubiquitous interfaces to all
participating software agents. Only generic semantics are encoded at
the interfaces. The interfaces should be universally available for
all providers and consumers.
-
Descriptive messages constrained by an extensible schema
delivered through the interfaces. No, or only minimal, system behavior
is prescribed by messages. A schema limits the vocabulary and structure
of messages. An extensible schema allows new versions of services to be
introduced without breaking existing services.
As illustrated in the power adapter example, interfacing is
fundamentally important. If interfaces do not work, systems do not work.
Interfacing is also expensive and error-prone for distributed
applications. An interface needs to prescribe system behavior, and this
is very difficult to implement correctly across different platforms and
languages. Remote interfaces are also the slowest part of most
distributed applications. Instead of building new interfaces for each
application, it makes sense to reuse a few generic ones for all
applications.
Since we have only a few generic interfaces available, we must express
application-specific semantics in messages. We can send any kind of
message over our interfaces, but there are a few rules to follow before we
can say that an architecture is service oriented.
First, the messages must be descriptive, rather than instructive,
because the service provider is responsible for solving the problem. This
is like going to a restaurant: you tell your waiter what you would like to
order and your preferences but you don't tell their cook how to cook your
dish step by step.
Second, service providers will be unable to understand your request if
your messages are not written in a format, structure, and vocabulary that
is understood by all parties. Limiting the vocabulary and structure of
messages is a necessity for any efficient communication. The more
restricted a message is, the easier it is to understand the message,
although it comes at the expense of reduced extensibility.
Third, extensibility is
vitally important. It is not difficult to understand why. The world is an
ever-changing place and so is any environment in which a software system
lives. Those changes demand corresponding changes in the software system,
service consumers, providers, and the messages they exchange. If messages
are not extensible, consumers and providers will be locked into one
particular version of a service. Despite the importance of extensibility,
it has been traditionally overlooked. At best, it was regarded simply as a
good practice rather than something fundamental. Restriction and
extensibility are deeply entwined. You need both, and increasing one comes
at the expense of reducing the other. The trick is to have a right
balance.
Fourth, an SOA must have a mechanism that enables a consumer to
discover a service provider under the context of a service sought by the
consumer. The mechanism can be really flexible, and it does not have to be
a centralized registry.