Service Abstraction
The Service Abstraction principle in essence advocates regulated information sharing when it
comes to information about a service’s behavior, architecture, and
implementation. The objective of this principle is to turn services into
black boxes so that their implementations can be modified or evolved
independently from service consumers that may already be using the
service’s capabilities.
When applying this
principle, we classify the common types of information available about a
service into four categories referred to as meta abstraction types (Figure 1):
1. | functional information
|
2. | technology information
|
3. | programmatic information
|
4. | quality-of-service information
|
Of these four types of meta
information, usually technology and quality-of-service details make
their way into actual published contracts. It is generally considered
unnecessary for service consumer designers to need access to the rest of
the information. Steps are therefore taken to intentionally hide or
limit access to this information so as to avoid
consumer-to-implementation coupling and the temptation for service
consumers to further optimize their programs in relation to how the
service architecture currently exists.
Service Abstraction is also applied at a more detailed level to the actual service contract
itself, as explained in the upcoming Validation Abstraction pattern description.
Validation Abstraction
Regardless
of whether you are building services as Web services or REST services,
you can define message schemas with data types and validation logic that
establish specific constraints and a specific level of validation
granularity.
Strongly typed service
contracts and data models have the benefit of sparing services from
having to process messages that do not comply to certain types of
baseline data standards. On the other hand, there can be long-term
consequences to publishing this type of granular validation logic in
public service contracts used by service consumers. Namely, if the logic
needs to change (due to changes in overarching business requirements or
otherwise), then there may be the need to introduce new versions of the
service contract and assume the governance responsibilities that come
along with that circumstance.
The Service Abstraction
principle asks us to consider how much detail we absolutely need to
publish in service contracts and encourages us to trim away any
unnecessary constraints in support of optimizing the service contract
for increased longevity and reduced governance burden (Figure 2).
The flipside of this
approach is that the validation logic may need to be deferred to an
intermediary or inside the service logic itself.
The primary objective of Validation Abstraction is to further protect services and their consumers from the impacts of
change. When you have reason to believe that some parts of a message
structure may be volatile, there is good reason to consider not hard
coding those parts into a message schema.
For example, let’s say that you have a type called ApplicationClassType containing an enumeration of different classes that an application pertains to:
Example 1.
<xsd:simpleType name="ApplicationClassType"> <xsd:restriction base="xsd:string"> <xsd:enumeration value="CDA"/> <xsd:enumeration value="TRU"/> <xsd:enumeration value="CMI"/> <xsd:enumeration value="TER"/> <xsd:enumeration value="LBR"/> <xsd:enumeration value="LBA"/> <xsd:enumeration value="MAG"/> <xsd:enumeration value="IST"/> </xsd:restriction> </xsd:simpleType>
|
If you need to add an enumeration value to this list, you will need to create a new version of the schema, as follows:
Example 2.
<xsd:simpleType name="ApplicationClassType"> <xsd:restriction base="xsd:string"> <xsd:enumeration value="CDA"/> <xsd:enumeration value="TRU"/> <xsd:enumeration value="CMI"/> <xsd:enumeration value="TER"/> <xsd:enumeration value="LBR"/> <xsd:enumeration value="LBA"/> <xsd:enumeration value="MAG"/> <xsd:enumeration value="IST"/> <xsd:enumeration value="GER"/> </xsd:restriction> </xsd:simpleType>
|
This is considered a backwards-compatible change (as per Compatible Change ),
meaning you may not need to force consumers to be updated after the
change is made. However, if a consumer wants to use the new value
(“GER”) it may need to update in order to be made aware of its
existence. A non-backwards-compatible change, such as if MAG were no
longer a valid ApplicationClassType, will require that existing
consumers be updated.
A means of loosening this constraint when applying Validation Abstraction could be to specify the ApplicationClassType as follows:
Example 3.
<xsd:simpleType name="ApplicationClassType"> <xsd:restriction base="xsd:string"> <xsd:length value="3"/> </xsd:restriction> </xsd:simpleType>
|
This way, when you add GER to
the list of accepted values you will not need to change the structure of
any messages. The downside of this approach is that you have to solve
two things, namely validating the ApplicationClassType field inside the service logic and informing service consumers of when the range of acceptable values changes.
One way of informing consumers
without having to rely on human communication is to expose a separate
service capability that returns the most current list of values for the ApplicationClassType. This list can then be retrieved and cached inside consumers to avoid repeated communication overhead.
The validation of the field can be performed by creating a class that produces a list of ApplicationClass values and then creating an extension method that takes a string and checks if it is part of the list of values:
Example 4.
public static class Validators { private static List<string> listOfApplicationClass; static Validators() { listOfApplicationClass = GetListOfApplicationClass(); } private static List<string> GetListOfApplicationClass() { var list = new List<string>(); list.Add("CDA"); list.Add("TRU"); list.Add("CMI"); list.Add("TER"); list.Add("LBR"); list.Add("LBA"); list.Add("MAG"); list.Add("IST"); list.Add("GER"); return list; } static List<string> ListOfApplicationClass { get { return listOfApplicationClass; } } static bool ApplicationClassIsValid (this string applicationClass) { return ListOfApplicationClass.Contains(applicationClass); } }
|
Inside your service implementation you could then validate the incoming string for the application class as shown here:
Example 5.
if (!applicationClass.ApplicationClassIsValid()) { ... return error message to consumer ... }
|
If the validation code inside your service implementation finds that the ApplicationClassType
field is not valid, an exception is raised, responding to the consumer
with an error message (and the consumer would need to be prepared to
handle this kind of error).
As demonstrated here, applying Validation Abstraction can actually result in extra development effort and more complex
service and consumer logic. This can be justified when the long-term
savings in governance effort and expense are considered worthwhile.