Chapter 4. Comparing Architecture Capabilities

In the last chapter I showed you how architecture patterns can help define basic architectural characteristics. In this chapter I take a similar approach, but instead of architecture characteristics I focus on the architecture capabilities that are described through the patterns. By looking at an architecture pattern you can tell if applications will likely be scalable, maintainable, and extensible, and if they will be relatively easy to develop, test, and deploy.

In this chapter I compare microservices and SOA by focusing on three major architectural capabilities—the size of the application each architecture pattern supports, the type of systems and components that can be integrated using each architecture pattern, and finally the ability of the architecture pattern to support contract decoupling.

Application Scope

Application scope refers to the overall size of the application that an architecture pattern can support. For example, architecture patterns such as the microkernel or pipeline architecture are better suited for smaller applications or subsystems, whereas other patterns such as event-driven architecture are well-suited for larger, more complex applications. So where do the microservices and SOA patterms fit along this spectrum?

SOA is well-suited for large, complex, enterprise-wide systems that require integration with many heterogeneous applications and services. It is also well-suited for applications that have many shared components, particularly components that are shared across the enterprise. As such, SOA tends to be a good fit for large insurance companies due to the heterogeneous systems environment and the sharing of common services—customer, claim, policy, etc.—across multiple applications and systems.

However, workflow-based applications that have a well-defined processing flow and not many shared components (such as securities trading) are difficult to implement using the SOA architecture pattern. Small web-based applications are also not a good fit for SOA because they don’t need an extensive service taxonomy, abstraction layers, and messaging middleware components.

The microservices pattern is better suited for smaller, well-partitioned web-based systems rather than large-scale enterprise-wide systems. The lack of a mediator (messaging middleware) is one of the factors that makes it ill-suited for large-scale complex business application environments. Other examples of applications that are well-suited for the microservices architecture pattern are ones that have few shared components and ones that can be broken down into very small discrete operations.

In some cases you might find that the microservices pattern is a good initial architecture choice in the early stages of your business, but as the business grows and matures, you begin to need capabilities such as complex request transformation, complex orchestration, and heterogeneous systems integration. In these situations you will likely turn to the SOA pattern to replace your initial microservices architecture. Of course, the opposite is true as well—you may have started out with a large, complex SOA architecture, only to find that you didn’t need all of those powerful capabilities that it supports after all. In this case you will likely find yourself in the common position of moving from an SOA architecture to microservices to simplify the architecture.

Heterogeneous Interoperability

Heterogeneous interoperability refers to the ability to integrate with systems implemented in different programming languages and platforms. For example, you might have a situation in which a single complex business request requires the coordination of a Java-based application, a .NET application, and a Customer Information Control System (CICS) COBOL program to process the single request. Other examples include a trading application implemented in the .NET platform that needs to access an AS400 to perform compliance checks on a stock trade, and a Java-based retail shop that needs to integrate with a large third-party .NET warehousing system.

These examples are found everywhere in most large companies. Many banking and insurance systems still have a majority of the backend core processing in COBOL mainframe applications that must be accessed by modern web-based platforms. The ability to integrate with multiple heterogeneous systems and services is one of the few areas where microservices architecture takes a back seat to SOA.

The microservices architecture style attempts to simplify the architecture pattern and corresponding implementations by reducing the number of choices for services integration. REST and simple messaging are two of the most common remote-access protocols used. SOA, has no upper limit and promotes the proliferation of multiple heterogeneous protocols through its messaging middleware component.

The microservices architecture style supports protocol-aware heterogeneous interoperability. With protocol-aware heterogeneous interoperability, the architecture can support multiple types of remote-access protocols, but the communication between a particular service consumer and the corresponding service that it’s invoking must be the same (e.g., REST). As illustrated in Figure 4-1, the fact that the remote-access protocol between the service consumer and service is known does not necessarily mean that the implementation of either is known or has to be the same. With REST, for example, the service consumer could easily be implemented in C# with .NET, whereas the service could be implemented in Java. However, with microservices, the protocol between the service consumer and service must be the same because there is no central middleware component to transform the protocol.

Figure 4-1. Protocol-aware heterogeneous interoperability

SOA also supports protocol-aware heterogeneous interoperability, but it takes this concept one step further by supporting protocol-agnostic heterogeneous interoperability. With protocol-agnostic heterogeneous interoperability, the service consumer is ignorant not only of the implementation of the service, but also of the protocol the service is listening on. For example, as illustrated in Figure 4-2, a particular service consumer written in C# on the .NET platform may invoke a corresponding service using REST, but the service (in this case an EJB3 bean) is only able to communicate using RMI. Being able to translate the consumer protocol to the service protocol known as protocol transformation is supported through the use of a messaging middleware component. Again, since a microservices architecture has no concept of a messaging middleware component, it does not support the concept of protocol-agnostic heterogeneous interoperability.

Figure 4-2. Protocol-agnostic heterogeneous interoperability

If you find yourself in a heterogeneous environment where you need to integrate several different types of systems or services using different protocols, chances are that you will need to look toward SOA rather than microservices. However, if all of your services can be exposed and accessed through the same remote-access protocol (e.g., REST), then microservices can be the right choice. In either case, this is one area where you need to know your interoperability requirements prior to selecting an architecture pattern.

Contract Decoupling

Contract decoupling is the holy grail of abstraction. Imagine being able to communicate with a service using different data in a message format that differs from what the service is expecting—that is the very essence of contract decoupling.

Contract decoupling is a very powerful capability that provides the highest degree of decoupling between service consumers and services. This capability allows services and service consumers to evolve independently from each other while still maintaining a contract between them. It also helps give your service consumers the ability to drive contract changes using consumer-driven contracts, thereby creating a more collaborative relationship between the service and the service consumer.

There are two primary forms of contract decoupling: message transformation and message enhancement. Message transformation is concerned only about the format of the message, not the actual request data. For example, a service might require XML as its input format, but a service consumer decides to send JSON payload instead. This is a straightforward transformation task that is handled very nicely by most of the open source integration hubs, including Apache Camel, Mule, and Spring Integration.

Things tend to get a bit more complicated when the data sent by a service consumer differs from the data expected by the corresponding service. This impedance mismatch in the actual contract data is addressed through message enhancement capability. Whereas message transformation is concerned about the format of the request, message enhancement is concerned about the request data. This capability allows a component (usually a middleware component) to add or change request data so that the data sent by the service consumer matches the data expected by the service (and vice versa).

Consider the scenario in which a service consumer is sending some data as a JSON object for a simple stock trade. In this example, the service consumer is invoking a service by sending a customer ID, a CUSIP number identifying the stock to be traded, the number of shares to be traded, and finally the trade date in MM/DD/YY format:

{"trade": {
  "cust_id": "12345",
  "cusip": "037833100",
  "shares": "1000",
  "trade_dt": "10/12/15"
}} 

The service, on the other hand, is expecting data in XML format consisting of an account number, a stock symbol (assuming an equity trade), the shared to be traded, and the trade date in YYYY.MM.DD format:

<trade>
  <acct>321109</acct>
  <symbol>AAPL</symbol>
  <shares>1000</shares>
  <date>2015-10-12</date>
</trade>

When differences occur in the format of the contract between the service consumer and the service, it is usually the messaging middleware component or custom client adapter that performs the necessary data transformation and data-lookup functionality to make the different contracts work together. The diagram in Figure 4-3 illustrates this example. Database or cache lookups are performed to get the account number based on the customer ID and the symbol based on the CUSIP number. The date is also converted to a different format, and the shares are copied over to the new format since that field does not require any translation. This allows the service consumer to have a different contract from the service, so that when contract changes are made, they can be abstracted through the messaging middleware.

Figure 4-3. Contract decoupling

There are obviously some practical limitations to contract decoupling. If the data required by a service cannot be derived from another source or calculated using the data provided by the service consumer, the service call will fail because the service contract is not satisfied. Fortunately, lookup capabilities and basic transformations (such as date, time, and number fields) can usually fix most contract variances between service consumers and services.

An ongoing struggle in the IT industry is knowing how to prevent technology (the IT department) from driving the business. Whether you are performing a major software version upgrade of a large subsystem or replacing your accounting or customer management system, abstracting the interfaces and contracts through contract decoupling allows the IT department to make technology changes with no impact on the business applications across the enterprise. The stock trading scenario described earlier is a good example of this; swapping out a trading platform that uses CUSIP numbers to one that requires SEDOL numbers should not require all the business applications throughout the enterprise to change to SEDOL numbers.

Unfortunately, microservices must once again take a back seat to SOA with respect to this architecture capability. Microservices architecture does not support contract decoupling, whereas contract decoupling is one of the primary capabilities offered within a SOA. If you require this level of abstraction in your architecture, you will need to look toward a SOA solution rather than a microservices one for your application or system.

Get Microservices vs. Service-Oriented Architecture now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.