Do not tie your service interface or implementation to any specific business process. Instead, move business process-specific code into an orchestration or new process service.
A composed service is an aggregate of other existing services. While you can realize reuse for a service by invoking the same service from different applications, a service is composable if it is defined in such a way that it can be reused by inclusion within another service’s implementation.
There are two matters here: the service interface must not negatively impact its composability, and its implementation must not inadvertently do so either. In order to provide business value, a service must perform work specific to some business use case. But you must consider the distinction between the execution of a specific unit of work (a service call), and the workflow around that operation that makes it available and valuable to the business in the form of some given business application.
The general idea of composability in software is not very different from its colloquial use. Because of their design, Lego bricks can be used to form a variety of exciting shapes. A Java Address class can be used to compose both an Employee object and a Company object. Likewise, the composability of services must be included as an initial consideration in the design process.
Consider a software company. There programmers type code on different, specific aspects of the system—one for user interface, one for database, and so on. These are roughly analogous to the member services. Then there are the programming managers. They don’t type anything into an IDE, but they coordinate the activities of their employees to ensure that eventually all of the programmers’ efforts are incorporated as features in the single software product that customers are interested in. Each programmer does important work on his own, but the addition of the manager presents an interface to the business that coordinates the activities, as a process service might. The process service doesn’t do any independent work: its single function is to direct the activities of other services, and in so doing, creates something new.
In a more service-oriented example, imagine you are a retail company, and need to write a service that your point of sale application will invoke to perform pre-approvals of a customer’s credit during checkout. If the customer is credit-worthy, your salesperson can offer her a discount for filling out your private label credit card application. Your requirements dictate that you must first check your customer database to determine if the customer already has your credit card, so the salesperson doesn’t offer her something she already has.
If you already have a Customer lookup service that indicates, among other things, whether a customer has your private label card, that service can perhaps be reused by, and composed into, your CreditCheck service. That assumes that the Customer lookup service was designed for general purposes and includes all of the relevant data. Likewise, you want the service that performs the basic CreditCheck to do only the credit check, and nothing else. You don’t want that service itself checking the local customer database, even if it is doing so by reusing the existing customer lookup service. Doing so would dilute the composability of the CreditCheck. In object-oriented programming, this idea is called “high cohesion”—the design restriction that dictates that the operations of a class must be very closely related.
It may seem that the customer lookup and the credit check are closely related, but that is debatable. Getting customer data is a simple operation, with no apparent business case other than the fact that you need to know stuff about your customers. Likewise, the act of performing a credit check says absolutely nothing about whether you must first check your local customer database to see if the customer already has your card. That is a requirement that a specific IT project must fulfill, but it is not relevant to the act of checking credit. To conflate these ideas is to make a category mistake, and limit the reusability and flexibility of your SOA.
So now you are left with three separate ideas and therefore three separate services:
The Customer lookup service that you already had and which you designed to be free of any particular use case, so it is available for further composition.
The basic credit check service that simply hits the credit bureau with a name and address and returns a result.
The overall credit check process service as described by the business analyst, which does no work itself other than coordinating the activities of the worker (member) services.
It is the job of this third composing service to coordinate the activities in such a way that the business requirements are fulfilled. That is, it is the job of the workflow to know that you want to check if the customer already has a card before wasting cycles invoking the credit check service. In this workflow, this is the consumer-facing service, and consumers will be wholly unaware of how their result is delivered behind the scenes.
This is useful because it helps push the business requirements that are incidental to this particular use case up into a customer view that is specific to this use case, allowing the member services to retain their independence. It is possible, for example, that you want to reuse the CreditCheck service in some new way. Your business may want to perform credit checks of prospective new hires, who would not be in your customer database. This would be difficult to deal with had the customer check been baked in at the service level. A frequent goal in object-oriented programming is to isolate what might change; it’s the same thing here.
The bottom line: the business process service must be defined outside the worker service, which can be other process services, or entity or functional services. Services are not always composable, but as you design your services, remember that composability is something to strive for; at the least, perform a check to see if your evolving design helps or hurts your possibility for composition, even if you don’t see an immediate need.
Note that service composition is particularly hard on runtimes. It is important to ensure that your SLAs are always met, and this becomes somewhat tricky when you have a set of services composing a variety of basic services that also compose to varying degrees another set of services.
Chapter 9 covers how to define and create orchestrations technically.