A Practical Guide to Enterprise Architecture

We have examined the benefits of an SOA; let's now take an abstract look at Service-Oriented Architecture.

In general, SOAs have the following characteristics:

  • Services have well-defined interfaces (contract) and policies.

  • Services usually represent a business function or domain.

  • Services have a modular design.

  • Services are loosely coupled.

  • Services are discoverable and support introspection.

  • The location of services is transparent to the client.

  • Services are transport independent.

  • Services are platform independent.

SOA can be implemented in many different ways. Currently the most common and visible SOA is Web services. In fact, many articles equate SOA and Web services, but that is incorrect. Web services have specialized the SOA to fit an Internet implementation. The purpose of this chapter is to acquaint the reader with the full definition of Service-Oriented Architecture.

Services Have Well-Defined Interfaces and Policies

Well-defined interfaces (contract) are a central concept for SOAs. All services publish a contract. This contract encapsulates the agreement between the client of the service and the service. Contracts are what the consumer peruses when searching for a service. The contract contains all the information necessary to create an application that is capable of accessing the service. Using the information in the contract to access and utilize the service is called binding.

Contracts are a new concept to many IT professionals. The old paradigm, carried over from procedural programming days from object-oriented development (OOD), is that applications are a set of classes that provide the desired functionality. Classes are organized into inheritance hierarchies with subclasses inheriting both data members and functionality from their chain of parent classes. Applications are built from a collection of classes that interact with each other via their functions. These functions consist of a (hopefully) descriptive name and a function signature. The function signature states what data type the class will return and what data it requires as input. Function signatures imply synchronous, tightly coupled, procedures called interactions. Classes provide a type to which to bind during the compile.

This system of classes and subclasses has several limitations. Tight coupling introduces rigidity and makes future enhancements harder to implement, which is undesirable in a software system of any size. If changes are made in the parent, it can affect the functionality of the children. Incorporating functionality from more than one superclass involves multiple inheritance, which introduced even more fragility. Some object-oriented (OO) languages, Java being one of them, do not support multiple inheritance.

To answer some of the problems with straight inheritance, interfaces were developed. Interfaces allow the specification of an interface contract (method signature and return type) without implementing any functionality. Interfaces outline the design for the object in pure terms, devoid of any implementation details. In OOD, a class that subclasses another inherits data and functionality from its parent. Interfaces, on the other hand, have no associated implementation. This places an important level of indirection between the consumer and the provider. Any class can declare that it implements an interface, making it much easier to change the subunit that is providing the service. Another way of thinking about interfaces is that they specify an interface contract, not an object. An interface is moving in the direction of the SOA concept of a contract. Interfaces provide the following to the consumer:

  • A data type

  • Expected output

  • Required inputs

  • Error messages (usually exceptions that can be thrown)

Systems of distributed objects, such as CORBA and DOM, make extensive use of interfaces. Interface Definition Language (IDL), a standard language to allow different programming languages to specify interfaces and their characteristics, has been created.

SOA is built on the idea of a consumer and a service being tied by a contract. SOA contracts considerably extend the idea of interfaces. The SOA contract specifies the following:

  • Functionality provided

  • Required inputs and expected outputs

  • Preconditions

  • Post-conditions

  • Error handling

  • Quality of service guarantees and SLAs (optional)

A service ideally should have two interfaces. The technical interface was discussed previously. We refer to the second interface as the business policy interface. The producers and consumers of a service both define policies around reliability, availability, security, and exception policies.

Functionality

The functionality provided specifies exactly what the service agrees to accomplish. For Canaxia, examples of functionality are transfer vehicles to dealers, get inventory status, and get current financial balance sheet. Functionality will normally map to a business process or a subunit of a business process. Occasionally it will be a technical service that is provided, such as authentication and authorization.

Expected Inputs and Outputs

What the service expects as input and what it agrees to provide as output are important additional considerations for a client. It is better to think in terms of interfaces when thinking of inputs and outputs rather than the traditional semantics of function calls.

Pre- and Post-Conditions

Preconditions are inputs or application states that must exist prior to a service being invoked. A common precondition is that the client has been authorized through a security mechanism. This could be represented as a requirement for a security token to be part of the input or for information required for the service to authenticate a user and obtain authorizations.

Post-conditions are the state of the service after the request has been processed. If the service has been called as part of a transaction, that state could be that the service should not commit the transaction until notified to do so by a transaction coordinator. Post-conditions that are absolutely vital to nail down are how the service will respond to errors. The state of the system after an error is encountered could well depend on the exact error encountered, so it is extremely important that all this be spelled out in the post-conditions section of the contract.

Error Handling

Error handling is another area that should be established in the contract. From a modeling point of view, an error is an alternate use case or path through the process. All error paths do not return the product of value that the actor desires. If an error condition is encountered in the operation of the service, the client will most likely expect to receive some structure that contains a useful description of the error or errors encountered. Usually that error structure will consist of an XML string or document.

Quality of Service Agreements and SLAs

Quality of service (QoS) agreements and SLAs are optional but very important components of an SOA contract. These are specifications on matters such as performance, multithreading, fault tolerance, and so on. A consumer could well base his or her choice of a provider on the service levels and guarantees that the provider can offer. The ability to provide QoS or SLA information for a consumer to browse is an important aspect of SOA.

The registry ties everything together. It is a place where the service deposits or registers and where the consumers go to look for a contract that fulfills their needs. The term registry has many meanings. A registry provides a mechanism for consumers to find contracts based on specified search criteria. Consumers then will bind to the service, as shown in Figure 3-1.

Figure 3-1. SOA contract mechanism.

Contracts do not contain any information on the location of the service (endpoint). This is an important point. To tie endpoint information to the contract would introduce implementation details into the contract that do not belong there. The information necessary to locate to the desired service is in another part of the registry. For example, a Canaxia service consumer gets a list of services from the Canaxia registry. When it has made its choice of services, it passes the fully qualified service name to the service lookup function of the registry, and that part of the registry returns an endpoint to use to actually locate and use the service.

Contracts are not just a way station on the path to accessing a service; they contain information that can be a significant part of the run-time relationship between consumer and provider:

  • The interface information that contracts contain also can be used as part of a process that dynamically builds the client's proxy layer.

  • Contracts can contain XML specifications for the format of the data coming in and out of the service.

Registry

The concept of a registry is central to the SOA. A registry provides a catalog of available service providers, a mechanism to iterate through and examine what a service provider has to offer, and a facility to provide an object (an endpoint) to which the consumer of the service can bind. All these functions can be combined in the same object, or they can be split into a registry that deals exclusively with contracts and a run-time registry that provides endpoints. This registry can be maintained and provided by the enterprise, by independent sources, or by other businesses that have services to provide. All registries must implement an application programming interface (API) that allows services to be registered and consumers to discover service providers and bind to them. If you expect outside entities to use your services, the registry API should conform to open standards.

Registry Coordinator

Leaving the task of publishing the service's contract to the developer or architect of the service could produce duplicate services (functionally equivalent services with different interfaces) or services that offer less than the best business functionality. If your company is setting up its own registry, you should consider creating a coordinator position to control the contents of the directory. Ideally this person will be someone with broad exposure to business concepts.

Registries are the central mechanism for separating consumers and providers. This separation allows an SOA to increase capacity on demand and to provide continuous availability of services. It can also allow contextual routing, if desired.

Registries don't have to contain the contracts. They can contain descriptions of what the service provider offers and pointers to the location of the actual contract. This allows the service provider to maintain the contract information locally, where it is easier to update.

Services Represent a Business Domain

Services can be established to represent a variety of problem domains. A service can represent either a business domain or a technical domain. One of the most common types of services are ones that provide technical functionality. Registries, security providers, and logging are a few of the infrastructure and technical applications that make excellent services.

However, the real power of an SOA lies in its ability to model a business domain. Business services are normally more difficult to implement than technical services but more valuable internally and externally. Producing services that represent a significant business process is the real lasting value of an SOA.

Ideally, the architecture team would sit down with the business customer and produce a business model of the business process that is slated to become a service in an SOA. Output of this process could be UML: use cases, class, sequence, and activity diagrams coupled with a business-process-model workflow diagram.

As part of employing modular design, all the services should completely encapsulate a business domain or problem. Once more, the object of this exercise is to reduce or eliminate dependencies among different services. If your services are dependent on other services, changes in the other service can potentially cascade to your service. This could force you to redesign some of the modules in your service. At the very least, it will force you to perform a complete regression test on your service. Beyond the practical considerations of application maintenance, forcing services to represent a business domain greatly facilitates the mapping of application architecture to business architecture (McGovern et al. 2003).

The process of mapping of business processes into services is the beginning of the process of the company defining its business architecture. This synergistic interplay of better definition of business architecture leading to a better definition of the service can be a major impetus to the company's further pursuit of business modeling.

As the business domains that are realized in your SOA become larger and more complex, you will find yourself building the totality of a business process from a multitude of smaller services. These services will be tied together by messages and operate in a sequence or a flow. The concept of service composition is being developed in the Web services space to allow for the enumeration and description of the component services that are combined to form the whole. Orchestration defines the message linkages and the workflow among component services. A business process execution language (BPEL) is being developed for Web services to allow the description of the orchestration of a complex business service. This will allow for the dynamic assembly of small services into complex business processes. In addition, work is being done to allow orchestration and compositions to be generated from BPELs. The end result of this is to allow an SOA to speed the realization of business processes considerably and to make the modification of the business process assemblage dynamic and almost painless.

For choreography, please see lists.w3.org/archives/public/www-ws-arch/2003Feb/att-0212/02-wd-wsa-arch-review.htm.

For the Web service initiatives to BPEL4WS (www.oasis-open.org/cover/bpel4ws.html).

Services Have a Modular Design

Services are composed of modules (Parnas 1972). Modular design is important to SOAs. A module can be thought of as a software subunit or subsystem that performs a specific, well-defined function. For the purposes of our discussion, a module is a set of software units that provides value to a service consumer. A module should perform only one function, but it should perform that function completely. That is, the module should contain all the subunits or sub-modules necessary to completely perform required functions. In other words, modules should exhibit high internal cohesiveness.

Application of this rule brings up questions of granularity, the measurement of size of the functionality that a module performs. Fine-grained modules perform a single, small function. Coarse-grained components perform a multitude of functions. For example, a transfer of money in a banking system involves withdrawal from one account and deposit into another account. A fine-grained module would be one that only performs the withdrawal functionality. A coarser-grained module would be one that indivisibly combines both the withdrawal and the deposit functions. Keeping modules fine-grained promotes flexibility and reuse at the expense of performance. Coarse-grained modules are difficult to mix and match with other modules to produce new services. Modules should be fine-grained components. Use cases will most often be translated into modules.

For example, the movement of a vehicle from Canaxia's inventory to a dealer involves a series of interconnected steps. The data manipulation involved in each step is encapsulated into a fine-grained object. Then the appropriate fine-grained objects are aggregated into a single function. This is the process:

  1. On a weekly basis, orders from dealers are queried to ascertain what makes and models have been ordered.

  2. The vehicles destined for particular geographic areas are put together, and the transport company is notified that x numbers of cars are going to, for example, the Northwest.

  3. The transport company responds with a date and time that the car-transport trucks will be at the Canaxia plant.

  4. The day of shipment, pick lists are generated and assembly points for all the cars going on a particular truck are identified.

  5. When the cars have been successfully loaded on the transports, they are taken off Canaxia's inventory.

  6. All dealers receiving vehicles in this shipment are then notified of the makes and models that have been shipped, along with other shipping details.

  7. When the vehicles have arrived, dealers notify Canaxia and receipt is recorded in the proper database.

The individual steps should be encapsulated into fine-grained components. Then the fine-grained components should be aggregated into a single process. If, in the future, changes are made to any of the components, the rest of the module and any client accessing that module will remain unaffected.

Keeping these rules in mind will make it easier to design modules or to work with existing applications to prepare them to fit into an SOA environment. Putting too many functions into a module will make it awkward and difficult to replace or modify. If a module does not have all the functionality required to completely fulfill its purpose, other modules may be linked in to add the missing capabilities. That could introduce dependencies into the environment that will make it difficult to modify the module at a later date. We suggest keeping subunits fine-grained and using use cases to help specify the functionality of the module and those subunits that should be included within it.

Breaking functionality into modules allows for tremendous freedom in swapping modules in and out of a service. Being able to swap modules freely without requiring any changes to any of the other modules in the application or even a recompile of the other modules, is one of the things that gives an environment built on SOA principles a tremendous amount of reusability and room for growth.

Services Are Loosely Coupled

Service clients and service providers should be loosely coupled. Loose coupling means that there are no static, compile-time dependencies between clients and providers. Consumers dynamically find providers at run-time. Dynamic location of providers allows details of the service, such as physical location, clustering, and fail-over, to be completely hidden from the consumer. A proxy object is an effective mechanism to hide the details of the physical network location from the consumer. The consumer asks a proxy object for a handle to a service. The proxy object performs a lookup to ascertain the current physical location of that service and passes the request to that service, then returns the results to the client.

Having the service hide the internal details of how it functions to fulfill its duties also provides loose coupling. Knowledge of internal implementation details can lead to unnecessary dependencies between client and service. One very effective way to manage the exposure to clients of internal service details is by using the Façade or the Composite pattern (Gamma 1995). Façades and composites are particularly useful in providing the correct granularity for the service. As indicated previously, providing the correct granularity can be difficult. Forcing the user to interact with a myriad of fine-grained services is poor design and can lead to poor performance due to the many network calls required to complete a unit of work. Coarse-grained design, by aggregating too much functionality in a single service, reduces the flexibility and adaptability of an architecture. The use of a design pattern such as Façade or Composite allows the flexible aggregation of fine-grained components into what appears to be a coarse-grained component of just the right functionality.

We have previously discussed Canaxia's process of moving a vehicle from the factory inventory to the dealer. The modules that are involved in each step can be aggregated into a single service and put behind a contract that says a press of the button will prompt all orders to be filled. What you would have in this case is a stovepipe type of service. This is not a good design. Many steps in this process should be open to input and intervention. If the current factory inventory is insufficient to fill the orders, it should be possible to plug a prioritization system between step one and step two. Or, suppose that a big promotion event is being generated around the creation of the 1,000,000th Canaxia vehicle. A picture of the vehicle rolling off the assembly line would be produced, but the marketing department needs to get information from the components responsible for step 4 so that a picture of the 1,000,000th vehicle being loaded on the transport can be taken. Marketing may even want to intervene in the choice of which area gets the 1,000,000th vehicle. Multigrained service interfaces require proper support for the range of requests that might be made on the service. By keeping the actual modules that carry out the vehicle distribution fine grained, the architecture gains flexibility and modifiability. By putting an object that amalgamates all the functionality into a single operation, the service creates ease of use.

Another way to resolve the issue of granularity is the concept of multi-grain. Multigrained services combine fine-grained components to produce a coarse-grained result. See Java Web Services Architecture (McGovern, Tyagi, Stevens, and Mathew 2003) for a full discussion of multigrained services.

By adding access to some of the subassemblies that encapsulate the steps in the process, the service adds all the additional functionality that the business requires without sacrificing ease of use. The key to making all this possible is modularization and loose coupling.

Façade is one design pattern in which a single object provides a unified, high-level interface to a subsystem of components.

The Composite pattern composes objects into tree structures. Composites allow clients to treat individual objects in the composition uniformly.

We refer you to the seminal book on software design patterns, Design Patterns: Elements of Reusable Object-Oriented Software (Gamma 1995), for a more detailed discussion of the Façade and Composite patterns.

Services are meant to be loosely coupled. Therefore, there should be a preference for the service to leverage other services for common, clearly defined tasks such as authentication/authorization, reporting, and so on. This will enable support for orchestration.

Services Are Discoverable and Support Introspection

Another key to the flexibility and reusability of an SOA is the concept of dynamic discovery and binding. When the modules of the client are compiled, they have no static linkages with the service. Similarly, when the modules of the service are compiled, they have no static linkages with any clients. Rather than use static linkages built in at compile time, clients of an SOA use a registry to look up the functionality they desire. Then, using the access pointer obtained from the registry, the client gets the endpoint from the service and uses that information to access the service. Thus, the service is completely free to modify or move the module that fulfills the contract. Conversely, if the needs of the client change or a service provider withdraws its service offering, the client is completely free to choose another service. As long as both sides are using the same contract, the consumer can change service providers without having to make any changes in its internal workings. It is incumbent upon the service provider not to change its contract. Contracts, once published, must not be changed. To do so creates a risk of breaking any clients that are counting on using that service. Contracts can be versioned, and a graceful upgrade path from version to version can be designed and implemented.

Services can be provided on a time-limited basis, that is, they can be leased. Either the consumer or the provider can specify a time limit on the contract. When the lease on the contract expires, the consumer is forced to go back to the registry and rebind to the contract or choose a different one. This allows the consumer to avoid an unlimited lock into a particular contract without having to explicitly code for it. It also allows the provider of the contract a graceful way to revisit the service and decide if it wants to continue to provide the service and, if it is still interested in providing the service, to decide if it wants to change the conditions of the contract.

The scheme of going to a registry every time you wish to access a service can impose a substantial performance penalty on SOAs. The concept of a lease can be extended to allow the consumer to cache a connection and call it directly without having to go through a registry. In this situation, the lease is a guarantee on a reference, and while the lease is in force, the consumer can rely on being able to access it through this connection.

Services Are Independent of the Transport Mechanism

Clients access and utilize services using a network connection. SOAs are independent of the kind of network connection used to access services. Services are independent of the transport mechanism used to access them. In practice, this usually means that an adapter is created to allow access to the service via the various transports. Normally, adapters are created as needed. If clients wish to access the service via HTTP, an HTTP adapter is created. If another set of clients comes along that needs to access this service via RMI, the necessary RMI adapter is created. The same adapter can be used with multiple services, allowing reuse of this component. In addition, this architecture allows the service to transparently utilize new connection protocols as they are developed. In Web services, this is known as transport binding. The service client may choose to bind to a number of transport protocols that the service supports, as described by the Web Service Description Language (WSDL) contract definition for the service.

Ideally, adapters should be created that are not language specific, like RMI, as they break the notion of introspection.

Location of Service Is Transparent to Client

Making the location of the service transparent to the client allows tremendous flexibility in the implementation of an SOA. Since the client doesn't know or care where the service is located, the group tasked with providing the service can be located wherever it is most convenient. Instances of the service can be spread across several machines in a cluster. Enterprise reorganizations can move hosting of the service to a third-party provider. Outages and disruptions to one location can be concealed from the client by forwarding service requests to instances of the service running in entirely different locations. Making it unnecessary for the client to know the location of the service allows for an architecture that is much more flexible, maintainable, cheaper, and resistant to failure.

A Service Should be Platform Independent

For the greatest flexibility, a service shouldn't care upon which platform it is running. Therefore, the ideal service should theoretically be operating system independent.

In the real world, by making services independent of the transport, having an SOA that is platform independent is much less important. A .NET service running on Windows can, in theory, have access to clients running on any operating system via Web services.

The subject of interoperability of Java and .NET Web services is complex. Theoretically, since both sides are using transports that are very platform independent (HTTP, FTP, and SMTP) and they are communicating data via a relatively standard message (SOAP), no problems should arise when having a .NET consumer access a Java Web service and vice versa. Due to vague and undefined areas in the SOAP specification, there can be problems with applications built with one toolkit interoperating applications built with another toolkit. Interoperability issues are being addressed by soapbuilders.org.

Many enterprises run on a variety of platforms. Services developed for these corporations should be platform independent to provide the flexibility in hosting that is required for maximum business functionality. Services developed for smaller organizations, as well as businesses that are utilizing a single operating system, will not find being platform independence particularly important.

Категории