Chapter 3. Comparing Architecture Characteristics

A component is a unit of software that has a well-defined interface and a well-defined set of roles and responsibilities. Components form the building blocks of the architecture. For service-based architectures those building blocks are usually referred to as services (or service components). Regardless of the label you put on a component, when creating an architecture you will need to determine how components are shared, how they communicate, how they are combined to fulfill a particular business request, and how they are accessed from remote service consumers.

Determining all of this is not always an easy task. This is where architecture patterns come in. Each architecture pattern has a unique topology that defines the shape and general characteristics of the architecture, including how components relate, communicate, and act together to fulfill business requests. By analyzing the topology of the architecture pattern, you can better determine if the pattern is the right choice for you.

In this chapter I explore the differences between microservices and SOA in terms of the overall architecture topology and the defining characteristics of the architecture pattern. Specifically, I focus on the differences between the two patterns with respect to the level of service-component sharing, the level of service-component communication, and how remote service components are typically accessed. I also dive into the differences between the messaging middleware found in the SOA architecture pattern and the optional API layer found in the microservices architecture pattern.

Component Sharing

Microservices and SOA are inherently different when it comes to sharing components. SOA is built on the concept of a share-as-much-as-possible architecture style, whereas microservices architecture is built on the concept of a share-as-little-as-possible architecture style. In this section I explore the differences between these two concepts as they relate to microservices and SOA.

Component sharing is one of the core tenets of SOA. As a matter of fact, component sharing is what enterprise services are all about. For example, consider a large retail company, as illustrated in Figure 3-1, that has many applications associated with the processing of an order, such as a customer-management system, warehouse-management system, and order-fulfillment system. All of these systems have their own version of an Order service. In this example, let’s assume that the process to update an order requires special business logic. This means that the special processing logic needs to be replicated across several applications in the enterprise, requiring additional verification and coordination among these applications. The other thing to notice in Figure 3-1 is that each system in this example has its own database, so each system might have a sightly different representation of an order.

Figure 3-1. Silo-based processing

SOA tries to address this problem through enterprise-level shared services (enterprise services). Continuing with our retail example, if a centrally shared Order enterprise service is created, as shown in Figure 3-2, every application can share the same processing logic associated with updating an order.

Figure 3-2. Service component sharing

Notice in Figure 3-2 that although the Order service is now shared, it still accesses three different databases (each one representing the respective system it is associated with). This is a critical concept in SOA when the share-as-much-as-possible architecture style is used. The Order service is smart enough to know which database to go to to retrieve and update order data for each system, at the same time synchronizing the data among all three systems. In other words, the order is represented not by one database, but by a combination of three databases.

Alhtough the concept of a share-as-much-as-possible architecture solves issues associated with the duplication of business functionality, it also tends to lead to tightly coupled components and increases the overall risk associated with change. For example, suppose you make a change to the Order service in Figure 3-2. Since the Order service is an enterprise service and available globally across the company, it is very difficult to test all possible uses of this global service to make sure the change isn’t affecting other areas of the enterprise.

Microservices architecture, being built on the concept of share-as-little-as-possible, leverages a concept from domain-driven design called a bounded context. Architecturally, a bounded context refers to the coupling of a component (or in this case, a service) and its associated data as a single closed unit with minimal dependencies. A service component designed this way is essentially self-contained and only exposes a well-defined interface and a well-defined contract.

Realistically, there will always be some services that are shared, even in a microservices architecture (for example, infrastructure services). However, whereas SOA tries to maximize component sharing, microservices architecture tries to minimize on sharing, through the concept of a bounded context. One way to achieve a bounded context and minimize dependencies in extreme cases is to violate the Don’t Repeat Yourself (DRY) principle and replicate common functionality across services to achieve total independence. Another way is to compile relatively static modules into shared libraries that service components can use in either a compile-time or runtime binding. My friend and colleague Neal Ford takes a slightly different view of this by saying that microservices architecture is a share-nothing architecture with the exception of two things—how services integrate with one another, and the infrastructure plumbing to ensure engineering consistency.

There are numerous advantages to leveraging the bounded context concept. Maintaining services becomes much easier because of the lack of dependent modules, allowing services to change and evolve independent of other services. Deployment becomes much easier as well because there is less code to deploy and also less risk that a change made to one module or service will affect other parts of the application. This in turn creates more-robust applications that have fewer side effects based on service changes.

Service Orchestration and Choreography

The difference between service orchestration and service choreography is unfortunately not always clear. In this section I describe the differences between orchestration and choreography and how these service communication concepts are used in both microservices and SOA.

The term service orchestration refers to the coordination of multiple services through a centralized mediator such as a service consumer or an integration hub (Mule, Camel, Spring Integration, etc.). The diagram in Figure 3-3 illustrates the concept of service orchestration.

Figure 3-3. Service orchestration

The easy way to think about service orchestration is to think about an orchestra. A number of musicians are playing different instruments at different times, but they are all coordinated through a central person—the conductor. In the same way, the mediator component in service orchestration acts as an orchestra conductor, coordinating all of the service calls necessary to complete the business transaction.

Service choreography refers to the coordination of multiple service calls without a central mediator. The term inter-service communication is sometimes used in conjunction with service choreography. With service choreography, one service calls another service, which may call another service and so on, performing what is also referred to as service chaining. This concept is illustrated in Figure 3-4.

Figure 3-4. Service choreography

One way to think about service choreography is to think about a dance company performing on stage. All of the dancers move in synchronization with one another, but no one is conducting or directing the dancers. Dances are choreographed through the individual dancers working in conjunction with one another, whereas concerts are orchestrated by a single conductor.

Microservices architecture favors service choreography over service orchestration, primarily because the architecture topology lacks a centralized middleware component. The diagram in Figure 3-5 shows that the overall architecture topology consists of only two major components—service components and, optionally, an unintelligent API layer. (I discuss the API layer and its role in the next section.) From an implementation standpoint, you may have other components such as a service registration and discovery component, a service monitoring component, and a service deployment manager, but architecturally those components would be considered infrastructure services as part of the service taxonomy of the microservices architecture pattern.

Figure 3-5. Microservices architecture topology

Because microservices architecture is a share-as-little-as-possible architecture, you should try to minimize the amount of service choreography in your architecture, restricting interactions to those between functional services and infrastructure services. As I mentioned in the prior chapter, if you find that you need a lot of service choreography between your functional services, chances are your services are too fine-grained.

Too much service choreography in a microservices architecture can lead to high efferent coupling, which is the degree to which one component is dependent on other components to complete a single business request. Consider the example illustrated in Figure 3-6, which shows three services that are required to process an order request—validate order, place order, and notify customer. Architecturally, this business request has a high degree of efferent coupling, something architects strive to minimize in most microservices architectures.

Figure 3-6. Order processing service choreography example

This type of service coupling within service choreography can lead to poor performance and less robust applications. As I discussed in the prior chapter, since services are generally remote in a microservices architecture, each service call made while coordinating services using service choreography adds response time to the request due to the remote-access protocol communication and transport time. Furthermore, coordinating multiple services for a single business request increases the probability that a particular service in the call chain might be unavailable or unresponsive, resulting in a less reliable and robust application.

One solution to the issue of service choreography among functional services within a microservices architecture is to combine fine-grained services into a more coarse-grained service. If a fine-grained service happens to be shared among multiple services, you can either keep this as a separate service, or—depending on the size and nature of the functionality—violate the DRY principle and add that common functionality to each coarse-grained service.

Figure 3-7 shows how moving from three fine-grained services to one coarse-grained service eliminates the need for service choreography, thereby addressing three issues associated with service choreography. First, it increases overall performance because fewer remote calls are needed. Second, it increases overall robustness because fewer service availability issues occur. Finally, it simplifies overall development and maintenance by eliminating the need for remote service contracts.

Figure 3-7. Order processing consolidated service example

SOA, being a share-as-much-as-possible architecture, relies on both service orchestration and service choreography to process business requests. As illustrated in Figure 3-8, the messaging middleware component of SOA manages service orchestration by calling multiple enterprise services based on a single business service request. Once it is in the enterprise service, service choreography can be used to call application services or infrastructure services to help fulfill the particular business request.

Figure 3-8. SOA topology

Figure 3-8 also illustrates the variations that can occur within SOA with regard to service choreography. For example, an enterprise service may need to call an application service, and that application service may in turn need to call an infrastructure service to complete its business processing. Alternatively, the enterprise service may only need to call an application service or an infrastructure service directly, or the business logic may be self-contained within the enterprise service, thereby not requiring any service choreography.

The differences between microservices and SOA with regard to service orchestration and service choreography underscore many differences between the two patterns in architectural characteristics, including performance, development, testing, and deployment. Because SOA typically relies on multiple services (and service types) to complete a single business request, systems built on SOA tend to be slower than microservices and require more time and effort to develop, test, deploy, and maintain. In fact, these factors were some of the drivers that led architects away from SOA and more toward the simple and streamlined microservices architecture pattern.

Middleware vs. API Layer

If you compare Figure 3-5 and Figure 3-8 from the previous section you will notice that both architecture patterns appear to have a middleware component that handles mediation. However, this is not the case. The microservices architecture pattern typically has what is known as an API layer, whereas SOA has a messaging middleware component. In this section I compare these two components in terms of the roles they play and the capabilities they provide.

The microservices pattern does not support the concept of messaging middleware (e.g., integration hub or enterprise service bus). Rather, it supports the notion of an API layer in front of the services that acts as a service-access facade. Placing an API layer between your service consumers and the services is generally a good idea because it forms an abstraction layer so that service consumers don’t need to know the actual location of the service endpoints. It also allows you to change the granularity level of your services without impacting the service consumers. Abstracting service granularity does require a bit of intelligence and some level of orchestration within the API layer, but this can be refactored over time, allowing services to evolve without constant changes to the corresponding service consumers.

For example, let’s say you have a service that performs some business functionality related to product ordering. You decide that it is too coarse-grained, and you want to split the service into two smaller fine-grained services to increase scalability and ease deployment. Without an API layer abstracting the actual service endpoints, each service consumer using the service would have to be modified to call two services rather than just one. If you use an API layer, the service consumers don’t know (or care) that the single request is now going to two separate services.

SOA relies on its messaging middleware to coordinate service calls. Using messaging middleware (what I like to refer to as an integration hub) provides a host of additional architectural capabilities not found in the microservices architecture style, including mediation and routing, message enhancement, message transformation, and protocol transformation.

Mediation and routing describes the capability of the architecture to locate and invoke a service (or services) based on a specific business or user request. This capability is illustrated in Figure 3-9. Notice in the diagram the use of a service registry or service-discovery component, as well as the use of service orchestration. Both microservices and SOA share this capability, particularly with regard to a service registry or service-discovery component. However, with microservices service orchestration is typically minimized or not used at all, whereas with SOA it is frequently used.

Figure 3-9. Mediation and routing capability

Message enhancement describes the capability of the architecture to modify, remove, or augment the data portion of a request before it reaches the service. Examples of message enhancement include things like changing a date format, adding additional derived or calculated values to the request, and performing a database lookup to transform one value into another (such as a Committee on Uniform Security Identification Procedures [CUSIP] number into a stock symbol, and vice versa). The microservices pattern does not support this capability, primarily because it doesn’t include a middleware component to implement this functionality. SOA fully supports this capability through its messaging middleware. Figure 3-10 illustrates this capability. Notice in the diagram that the service consumer is sending a CUSIP number (a standard trading-instrument identifier) and a date in MM/DD/YY format, whereas the service is expecting a Stock Exchange Daily Official List (SEDOL) number (another type of trading instrument identifier), the date in YYYY.MM.DD format, and the stock symbol (in the event of an equity trade). In this case the messaging middleware can perform these enhancements to convert the CUSIP number for Apple, Inc. (037833100) into the SEDOL number for Apple, Inc. (2046251), look up and add the symbol (AAPL), and convert the date from 04/23/15 to 2015.04.23.

Figure 3-10. Message enhancement capability

Message transformation describes the capability of the architecture to modify the format of the data from one type to other. For example, as illustrated in Figure 3-11, the service consumer is calling a service and sending the data in JSON format, whereas the service requires a Java object. Notice that message enhancement is not concerned about the data of the request, but rather only about the format of the wrapper containing the data. Again, microservices architecture does not support this capability, but SOA does through the use of the messaging middleware.

Figure 3-11. Message transformation capability

Finally, protocol transformation describes the capability of the architecture to have a service consumer call a service with a protocol that differs from what the service is expecting. Figure 3-12 illustrates this capability. Notice in the diagram that the service consumer is communicating through REST, but the services invoked that are responsible for processing the request require an RMI/IIOP connection (e.g., Enterprise JavaBeans 3 [EJB3] bean) and an AMQP connection. Microservices can support multiple protocol types, but the service consumer and service must use the same protocol. In SOA, you can mix and match them as much as you want.

Figure 3-12. Protocol transformation capability

I discuss these capabilities in more detail in the next chapter as they relate to the comparison of architecture capabilities between microservices and SOA.

Accessing Remote Services

Since services are usually accessed remotely in microservices and SOA, these architecture patterns need to provide a way for service consumers to access the remote services. One of the fundamental differences between microservices and SOA with regard to remote access is that microservices architectures tend to rely on REST as their primary remote-access protocol, whereas SOA has no such restrictions. As a matter of fact, having the ability to handle dozens of different kinds of remote-access protocols is one of the main things that sets SOA apart from microservices.

One of the fundamental principles within microservices that contributes to the simplicity of the architecture pattern is that the number of technology and architecture choices is generally limited to a few options. For example, most microservices architectures usually rely on only two different remote-access protocols to access services—REST and simple messaging (JMS, MSMQ, AMQP, etc.). That’s not to say you couldn’t leverage other remote-access protocols such as SOAP or .NET Remoting, but the point is that the remote-access protocol found in microservices is usually homogeneous. In other words, services are either REST-based, messaging-based, or based on some other access protocol, but the access protocols rarely mixed within the same application or system. One exception to this is the case in which services that rely on publish-and-subscribe broadcast capabilities might be message-based, whereas other nonbroadcast services might be REST-based.

SOA has no pre-described limits as to which remote-access protocols can be used as part of the architecture pattern. As you will see in the next chapter, it is the messaging middleware component of the architecture that provides support for any number of remote access protocols, allowing for transformation from one protocol to another. That being said, most SOA architectures typically rely on messaging (e.g., JMS, AMQP, MSMQ) and SOAP as the primary service remote-access protocols. Depending on the scope and size of the SOA architecture, it’s not uncommon to use upwards of a half a dozen different remote-access protocols among heterogeneous services.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.189.185.42