Thinking of your apps strictly in terms of components can limit your reuse options. Find out how to increase your options by defining applications as services.
As I review my customers’ architectural designs for new systems based on the .NET platform, most of them follow a traditional two- or three-tier approach for creating their new applications. When we begin discussing which system artifacts they’ll reuse for future projects, very few can point beyond a few data tables or stored procedures.
The component interfaces they’re creating are tightly coupled to the business requirements imposed by the specific system they must also create, and they don’t see any way to get around these requirements without significantly impacting performance. It isn’t until we take a fresh look at the application with an eye toward the services provided by the application, rather than the components required to deliver it, that the benefits derived by using a services-oriented architecture (SOA) become clear.
What is an SOA?
In an SOA, the architect attempts to encapsulate the delivery of a specific set of tasks within a single entity that accepts a service request and returns the results of the work performed on its behalf or errors resulting from a failed attempt. The combination of these services, along with guidance on how they can be combined to complete an application, makes up an SOA. The key differences between the SOA architecture and a component-based architecture may be best explained by an example.
In a component-based architecture, the components will typically encapsulate specific entities and the business rules that operate on them. For example, an invoicing system based on component architecture may have a Customer component, an Invoice component, and a Products component. The developer creates these components to closely match the underlying entities (Customer, Invoice, and Products) and encapsulates behavior that matches the entity’s expected behavior.
In this example, AddCustomer(…) would allow you to add a customer, where InvoiceTotal(1001) would return the Total for Invoice 1001. The implementation of the individual component hides the method for performing the function, i.e., the user of the Invoice component doesn’t know whether the InvoiceHeader table has a TotalInvoice column or whether the component has to iterate through the individual entries in the InvoiceLineItems table to generate the Invoice total. In a component-based system, there will also be multiple layers of components that call each other and pass the data throughout the system using some kind of business entity. Component-based systems access shared data from back-end sources that they may or may not manage exclusively.
In an SOA, the functions aren't broken down by entities and component layers. Instead, we look at what’s necessary to implement all of the services that are required to manage operations across a set of entities. For example, a customer service would be able to respond to any request from any other system or service that needed to interrogate or manipulate customer information. The customer service could process a request to add a customer, check his or her credit history, or indicate his or her outstanding unpaid invoices. The customer service would own all of the data directly related to the customers it managed and would be able to make other service inquiries on behalf of the calling party in order to provide a unified customer service view.
A user interface program sending a service request to the customer service for outstanding unpaid invoices would get the information on the unpaid invoices from the invoice service, but the user wouldn’t have to call the invoice service directly—the user's authenticated request would be routed automatically to the invoice service, and the information would be provided for the user.
How do you implement an SOA using .NET?
Most early implementations of the SOA architecture don’t look radically different from their component-based alternatives. Many developers recognize the value of separating specialty services such as credit checking or document faxing. The implementation of these specialty services is basically an application with no UI. For example, a faxing service just needs to accept a document and a recipient list. How it manages the underlying delivery and confirmations from a business rules and data storage perspective should be inconsequential to the calling program. The calling program needs only a way to verify that all of the faxes were delivered based on the recipient list.
But even this simple example of implementing a specialty service is tightly coupled. The service expects the document and the recipient list in a very specific format, and the calling program expects its confirmation information to be returned via a status variable. To reap the ultimate benefits of an SOA, the implementation of the service must be done using a loosely coupled, message-based architecture.
For example, the document and recipient list aren’t sent directly to an application through a call-level interface; they are instead defined with an XML schema and a corresponding XML document, and are placed on an outgoing fax message queue. Then the message can be picked up by a single fax delivery Windows service—a workflow service that decomposes the queue message and forwards it to a set of Web services that can perform the necessary functions (e.g., rendering, sending, verification, etc.) and then report back to the workflow service. Upon completion, the fax delivery service places messages on the completion queue or on the incomplete queue to indicate the status of the request. Calling programs (or their delegates) can query the queues to perform actions necessary to finish the process. The implementation would be similar for a system designed to process customer requests.
The beauty of this type of implementation is that new message processing can be added quickly without making any change to the implementation of the existing interfaces. The architecture scales very well because you can either add processing power to existing machines providing these services, or you can add machines that would process specific messages when they appear on the queue. And if you base the message queuing interface on Web services architecture, any machine or process could call into your services architecture and submit or retrieve messages. The message-based SOA provides an additional layer of abstraction that eliminates many of the registration, recompilation, and interface-bound limitations of the classic component architecture.
The concept of the SOA and its implementation on a message-based architecture is a relatively new phenomenon that will continue to grow and develop over the next few years. I don’t expect that many companies will adopt this architecture for all of their new systems. But I think there are some significant advantages in using this architecture for specialty services now and for some early work in the area of entity-based services (e.g., customers and invoices).