Service Layer Guidelines
J.D. Meier, Alex Homer, David Hill, Jason Taylor, Prashant Bansode, Lonnie Wall, Rob Boucher Jr, Akshay Bogawat
- Understand how the service layer fits into the application architecture.
- Understand the components of the service layer.
- Learn the steps for designing the service layer.
- Learn the common issues faced when designing the service layer.
- Learn the key guidelines for designing the service layer.
- Learn the key patterns and technology considerations for designing the service layer.
When providing application functionality through services, it is important to separate the service functionality into a separate service layer. Within the service layer, you define the service interface, implement the service interface, and provide translator components that translate data formats between the business layer and external data contracts. One of the more important concepts to keep in mind is that a service should never expose internal entities that are used by the business layer. Figure 1 shows where a service layer fits into the overall design of your application. Figure 1 An overall view of a typical application showing the service layer
Service Layer Components
The service layer is made up of the following components:
- Service interfaces. Services expose a service interface to which all inbound messages are sent. The definition of the set of messages that must be exchanged with a service, in order for the service to perform a specific business task, constitutes a contract. You can think of a service interface as a façade that exposes the business logic implemented in the service to potential consumers.
- Message types. When exchanging data across the service layer, data structures are wrapped by message structures that support different types of operations. For example, you might have a Command message, a Document message, or another type of message. These message types are the “message contracts” for communication between service consumers and providers.
The approach used to design a service layer starts by defining the service interface, which consists of the contracts that you plan to expose from your service. Once the service interface has been defined, the next step is to design the service implementation; which is used to translate data contracts into business entities and to interact with the business layer.
The following steps can be used when designing a service layer:
- Define the Data and Message contracts that represent the schema used for messages.
- Define the Service contracts that represent operations supported by your service.
- Define the Fault contracts that return error information to consumers of the service.
- Design transformation objects that translate between business entities and data contracts.
- Design the abstraction approach used to interact with the business layer.
There are many factors that you should consider when designing the service layer. Many of these design considerations relate to proven practices concerned with layered architectures. However, with a service, you must take into account message-related factors. The main thing to consider is that a service uses message-based interaction, which is inherently slower than object-based interaction. In addition, messages passed between a service and a consumer can be routed, modified, or lost, which requires a design that will account for the non-deterministic behavior of messaging.
Consider the following guidelines when designing the service layer:
- Design services to be application-scoped and not component-scoped. Service operations should be coarse-grained and focused on application operations. For example, with demographics data, you should provide an operation that returns all of the data in one call. You should not use multiple operations to return subsets of the data with multiple calls.
- Design entities for extensibility. In other words, data contracts should be designed so that you can extend them without affecting consumers of the service.
- Compose entities from standard elements. When possible, use standard elements to compose the complex types used by your service.
- Use a layered approach to designing services. Separate the business rules and data access functions into distinct components where appropriate.
- Avoid tight coupling across layers. Use abstraction to provide an interface into the business layer. This abstraction can be implemented by using public object interfaces, common interface definitions, abstract base classes, or messaging. For Web applications, consider a message-based interface between the presentation and business layers.
- Design without the assumption that you know who the client is. You should not make assumptions about the client, or about how they plan to use the service that you provide.
- Design only for the service contract. In other words, you should not implement functionality that is not reflected by the service contract. In addition, the implementation of a service should never be exposed to external consumers.
- Design to assume the possibility of invalid requests. You should never assume that all messages received by the service will be valid.
- Separate functional business concerns from infrastructure operational concerns. Cross-cutting logic should never be combined with application logic. Doing so can lead to implementations that are difficult to extend and maintain.
- Ensure that the service can detect and manage repeated messages (idempotency). When designing the service, implement well-known patterns to ensure that duplicate messages are not processed.
- Ensure that the service can manage messages arriving out of order (commutativity). If there is a possibility that messages will arrive out of order, implement a design that will store messages and then process them in the correct order.
- Consider versioning of contracts. A new version for service contracts mean new operations exposed by the service, whereas for data contracts it means the addition of new schema type definitions.
Service Layer Frame
There are several common issues that you must consider as your develop your service layer design. These issues can be categorized into specific areas of the design. The following table lists the common issues for each category where mistakes are most often made.
|Area ||Key issues|
|Authentication and Authorization ||Lack of authentication across trust boundaries. |
| ||Lack of authorization across trust boundaries. |
| ||Granular or improper authorization. |
|Communication ||Incorrect choice of transport protocol. |
| ||Use of a chatty service communication interface. |
| ||Failing to protect sensitive data. |
|Exception Management ||Not catching exceptions that can be handled. |
| ||Not logging exceptions. |
| ||Not dealing with message integrity when an exception occurs. |
|Messaging Channels ||Choosing an inappropriate message channel |
| ||Failing to handle exception conditions on the channel. |
| ||Providing access to non-messaging clients. |
|Message Construction ||Failing to handle time-sensitive message content. |
| ||Incorrect message construction for the operation. |
| ||Passing too much data in a single message |
|Message Endpoint ||Not supporting idempotent operations. |
| ||Not supporting commutative operations. |
| ||Subscribing to an endpoint while disconnected. |
|Message Protection ||Not protecting sensitive data. |
| ||Not using transport layer protection for messages that cross multiple servers. |
| ||Not considering data integrity. |
|Message Routing ||Not choosing the appropriate router design. |
| ||Ability to access a specific item from a message. |
| ||Ensuring that messages are handled in the correct order. |
|Message Transformation ||Performing unnecessary transformations. |
| ||Implementing transformations at the wrong point. |
| ||Using a canonical model when not necessary. |
|REST ||Implementing state within the service. |
| ||Overusing POST statements. |
| ||Putting actions in the URI. |
| ||Using hypertext to manage state. |
|SOAP ||Not choosing the appropriate security model. |
| ||Not planning for fault conditions. |
| ||Using complex types in the message schema. |
Designing an effective authentication strategy for your service layer is important for the security and reliability of your application. Failure to design a good authentication strategy can leave your application vulnerable to spoofing attacks, dictionary attacks, session hijacking, and other types of attacks.
Consider the following guidelines when designing an authentication strategy:
- Identify a suitable mechanism for securely authenticating users.
- Consider the implications of using different trust settings for executing service code.
- Ensure that secure protocols such as Secure Sockets Layer (SSL) are used with Basic authentication, or when credentials are passed as plain text.
- Consider using secure mechanisms such as WS Security with SOAP messages.
Designing an effective authorization strategy for your service layer is important for the security and reliability of your application. Failure to design a good authorization strategy can leave your application vulnerable to information disclosure, data tampering, and elevation of privileges.
Consider the following guidelines when designing an authorization strategy:
- Set appropriate access permissions on resources for users, groups, and roles.
- Use URL authorization and/or file authorization when using Windows authentication.
- Where appropriate, restrict access to publicly accessible Web methods by using declarative principle permission demands.
- Execute services under the most restrictive account that is appropriate.
When designing the communication strategy for your service, the protocol you choose should be based on the deployment scenario your service must support. If the service will be deployed within a closed network, you can use Transmission Control Protocol (TCP) for more efficient communications. If the service will be deployed into a public-facing network, you should choose the HyperText Transfer Protocol (HTTP) protocol.
Consider the following guidelines when designing a communication strategy:
- Determine how to handle unreliable or intermittent communication.
- Consider using dynamic URL behavior with configured endpoints for maximum flexibility.
- Validate endpoint addresses in messages.
- Determine whether you need to make asynchronous calls.
- Determine if you need request-response or duplex communication.
- Decide if message communication must be one-way or two-way.
Designing an effective exception-management strategy for your service layer is important for the security and reliability of your application. Failure to do so can make your application vulnerable to denial of service (DoS) attacks, and can also allow it to reveal sensitive and critical information.
Raising and handling exceptions is an expensive operation, so it is important for the design to take into account the potential impact on performance. A good approach is to design a centralized exception management and logging mechanism, and consider providing access points that support instrumentation and centralized monitoring in order to assist system administrators.
Consider the following guidelines when designing an exception-management strategy:
- Do not use exceptions to control business logic.
- Design a strategy for handling unhandled exceptions.
- Do not reveal sensitive information in exception messages or log files.
- Use SOAP Fault elements or custom extensions to return exception details to the caller.
- Disable tracing and debug-mode compilation for all services, except during development and testing.
Communication between a service and its consumers consists of sending data through a channel. In most cases, you will use channels provided by your chosen service infrastructure, such as Windows Communication Foundation (WCF). You must understand which patterns your chosen infrastructure supports, and determine the appropriate channel for interaction with consumers of the service.
Consider the following guidelines when designing message channels:
- Determine appropriate patterns for messaging channels, such as Channel Adapter, Messaging Bus, and Messaging Bridge.
- Determine how you will intercept and inspect the data between endpoints if necessary.
When data is exchanged between a service and consumer, it must be wrapped inside a message. The format of that message is based on the types of operations you need to support. For example, you may be exchanging documents, executing commands, or raising events. When using slow message-delivery channels, you should also consider using expiration information in the message.
Consider the following guidelines when designing a message-construction strategy:
- Determine the appropriate patterns for message constructions, such as Command, Document, Event, and Request-Reply.
- Divide very large quantities of data into smaller chunks, and send them in sequence.
- Include expiration information in messages that are time-sensitive. The service should ignore expired messages.
The message endpoint represents the connection that applications use to interact with your service. The implementation of your service interface represents the message endpoint. When designing the service implementation, you must consider the possibility that duplicate or invalid messages can be sent to your service.
Consider the following guidelines when designing message endpoints:
- Determine relevant patterns for message endpoints, such as Gateway, Mapper, Competing Consumers, and Message Dispatcher.
- Determine if you should accept all messages or implement a filter to handle specific messages.
- Design for idempotency in your message interface. Idempotency is the situation where you could receive duplicate messages from the same consumer, but should only handle one. In other words, an idempotent endpoint will guarantee that only one message will be handled, and all duplicate messages will be ignored.
- Design for commutativity in your message interface. Commutativity is related to the order in which messages are received. In some cases, you may need to store inbound messages so that they can be processed in the correct order.
- Design for disconnected scenarios. For instance, you might need to support guaranteed delivery.
When transmitting sensitive data between a service and its consumer, you should design for message protection. You can use transport layer protection or message-based protection. However, in most cases, you should use message-based protection. For example, you should encrypt sensitive sections within a message and use a signature to protect the message from tampering.
Consider the following guidelines when designing message protection:
- If interactions between the service and the consumer are not routed through other services, you can use transport layer security only, such as SSL.
- If the message passes through one or more servers, always use message-based protection. In addition, you can also use transport layer security with message-based security. With transport layer security, the message is decrypted and then encrypted at each server it passes through, which represents a security risk.
- Consider using both transport layer and message-based security in your design.
- Use encryption to protect sensitive data in messages.
- Consider using digital signatures to protect messages and parameters from tampering.
A message router is used to decouple a service consumer from the service implementation. There are three main types of routers that you might use: simple, composed, and pattern-based. Simple routers use a single router to determine the final destination of a message. Composed routers combine multiple simple routers to handle more complex message flows. Architectural patterns are used to describe different routing styles based on simple message routers.
Consider the following guidelines when designing message routing:
- Determine relevant patterns for message routing, such as Aggregator, Content-Based Router, Dynamic Router, and Message Filter.
- If sequential messages are sent from a consumer, the router must ensure that they are all delivered to the same endpoint in the required order (commutativity).
- A message router will normally inspect information in the message to determine how to route the message. As a result, you must ensure that the router can access that information.
When passing messages between a service and consumer, there are many cases where the message must be transformed into a format that the consumer can understand. This normally occurs in cases where non–message-based consumers need to process data from a message-based system. You can use adapters to provide access to the message channel for a non–message-based consumer, and translators to convert the message data into a format that the consumer understands.
Consider the following guidelines when designing message transformation:
- Determine relevant patterns for message transformation, such as Canonical Data Mapper, Envelope Wrapper, and Normalizer.
- Use metadata to define the message format.
- Consider using an external repository to store the metadata.
Representational State Transfer (REST) represents an architecture style for distributed systems. It is designed to reduce complexity by dividing a system into resources. The resources and the operations supported by a resource are represented and exposed as a set of URIs over the HTTP protocol.
Consider the following guidelines when designing REST resources:
- Identify and categorize resources that will be available to clients.
- Choose an approach for resource representation. A good practice would be to use meaningful names for REST starting points and unique identifiers, such as a globally unique identifier (GUID), for specific resource instances. For example, http://www.contoso.com/employee/ represents an employee starting point. http://www.contoso.com/employee/8ce762d5-b421-6123-a041-5fbd07321bac4 uses a GUID that represents a specific employee.
- Decide if multiple representations should be supported for different resources. For example, you can decide if the resource should support an XML, Atom, or JSON format and make it part of the resource request. A resource could be exposed as both http://www.contoso.com/example.atom and http://www.contoso.com/example.json
- Decide if multiple views should be supported for different resources. For example, decide if the resource should support GET and POST operations, or only GET operations.
The service interface represents the contract exposed by your service. When designing a service interface, you should consider boundaries that must be crossed and the type of consumers who will be accessing your service. For instance, service operations should be coarse-grained and application scoped. One of the biggest mistakes with service interface design is to treat the service as a component with fine-grained operations. This results in a design that requires multiple calls across physical or process boundaries, which are very expensive in terms of performance and latency.
Consider the following guidelines when designing a service interface:
- Consider using a coarse-grained interface to batch requests and minimize the number of calls over the network.
- Design service interfaces in such a way that changes to the business logic do not affect the interface.
- Do not implement business rules in a service interface.
- Consider using standard formats for parameters to provide maximum compatibility with different types of clients.
- Do not make assumptions in your interface design about the way that clients will use the service.
- Do not use object inheritance to implement versioning for the service interface.
SOAP is a message-based protocol that is used to implement the message layer of a service. The message is composed of an envelope that contains a header and body. The header can be used to provide information that is external to the operation being performed by the service. For instance, a header may contain security, transaction, or routing information. The body contains contracts, in the form of XML schemas, which are used to implement the service.
Consider the following guidelines when designing SOAP messages:
- Define the schema for the operations that can be performed by a service.
- Define the schema for the data structures passed with a service request.
- Define the schema for the errors or faults that can be returned from a service request.
The service layer can be deployed on the same tier as other layers of the application, or on a separate tier in cases where performance and isolation requirements demand this. However, in most cases the service layer will reside on the same physical tier as the business layer in order to minimize performance impact when exposing business functionality.
Consider the following guidelines when deploying the service layer:
- Deploy the service layer to the same tier as the business layer to improve application performance, unless performance and security issues inherent within the production environment prevent this.
- If the service is located on the same physical tier as the service consumer, consider using the named pipes or shared memory protocols.
- If the service is accessed only by other applications within a local network, consider using TCP for communications.
- If the service is publicly accessible from the Internet, use HTTP for your transport protocol.
|Category ||Relevant patterns|
|Communication ||Duplex |
| ||Fire and Forget |
| ||Reliable Sessions |
| ||Request Response |
|Messaging Channels ||Channel Adapter |
| ||Message Bus |
| ||Messaging Bridge |
| ||Point-to-point Channel |
| ||Publish-subscribe Channel |
|Message Construction ||Command Message |
| ||Document Message |
| ||Event Message |
| ||Request-Reply |
|Message Endpoint ||Competing Consumer |
| ||Durable Subscriber |
| ||Idempotent Receiver |
| ||Message Dispatcher |
| ||Messaging Gateway |
| ||Messaging Mapper |
| ||Polling Consumer |
| ||Selective Consumer |
| ||Service Activator |
| ||Transactional Client |
|Message Protection ||Data Confidentiality |
| ||Data Integrity |
| ||Data Origin Authentication |
| ||Exception Shielding |
| ||Federation |
| ||Replay Protection |
| ||Validation |
|Message Routing ||Aggregator |
| ||Content-Based Router |
| ||Dynamic Router |
| ||Message Broker (Hub-and-Spoke) |
| ||Message Filter |
| ||Process Manager |
|Message Transformation ||Canonical Data Mapper |
| ||Claim Check |
| ||Content Enricher |
| ||Content Filter |
| ||Envelope Wrapper |
| ||Normalizer |
|REST ||Behavior |
| ||Container |
| ||Entity |
| ||Store |
| ||Transaction |
|Service Interface ||Remote Façade |
|SOAP ||Data Contracts |
| ||Fault Contracts |
| ||Service Contracts |
- Aggregator. A filter that collects and stores individual related messages, combines these messages, and publishes a single aggregated message to the output channel for further processing.**
- Behavior (REST). Applies to resources that carry out operations. These resources generally contain no state of their own, and only support the POST operation.
- Canonical Data Mapper.** Uses a common data format to perform translations between two disparate data formats.
- Channel Adapter.** A component that can access the application’s API or data and publish messages on a channel based on this data, and that can receive messages and invoke functionality inside the application.
- Claim Check.** Retrieves data from a persistent store when required.
- Command Message.** Provides a message structure used to support commands.
- Competing Consumer.** Sets multiple consumers on a single message queue and have them compete for the right to process the messages, which allows the messaging client to process multiple messages concurrently.
- Container.** Builds on the entity pattern by providing the means to dynamically add and/or update nested resources.
- Content Enricher.** Enriches messages with missing information obtained from an external data source.
- Content Filter.** Removes sensitive data from a message and reduces network traffic by removing unnecessary data from a message.
- Content-Based Router.** Routes each message to the correct consumer based on the contents of the message; such as existence of fields, specified field values, and so on.
- Data Confidentiality.** Uses message-based encryption to protect sensitive data in a message.
- Data Contract.** A schema that defines data structures passed with a service request.
- Data Integrity.** Ensures that messages have not been tampered with in transit.
- Data Origin Authentication.** Validates the origin of a message as an advanced form of data integrity.
- Document Message.** A structure used to reliably transfer documents or a data structure between applications.
- Duplex.** Two-way message communication where both the service and the client send messages to each other independently, irrespective of the use of the one-way or the request/reply pattern.
- Durable Subscriber.** In a disconnected scenario, messages are saved and then made accessible to the client when connecting to the message channel in order to provide guaranteed delivery.
- Dynamic Router.** A component that dynamically routes the message to a consumer after evaluating the conditions/rules that the consumer has specified.
- Entity.* (REST)*. Resources that can be read with a GET operation, but can only be changed by PUT and DELETE operations.
- Envelope Wrapper.** A wrapper for messages that contains header information used, for example, to protect, route, or authenticate a message.
- Event Message.** A structure that provides reliable asynchronous event notification between applications.
- Exception Shielding.** Prevents a service from exposing information about its internal implementation when an exception occurs.
- Façade. Implements a unified interface to a set of operations in order to provide a simplified reduce coupling between systems.
- Fault Contracts.** A schema that defines errors or faults that can be returned from a service request.
- Federation.** An integrated view of information distributed across multiple services and consumers.
- Fire and Forget.** A one-way message communication mechanism used when no response is expected.
- Idempotent Receiver.** Ensures that a service will only handle a message once.
- Message Broker (Hub-and-Spoke).** A central component that communicates with multiple applications to receive messages from multiple sources, determines the correct destination, and route the message to the correct channel.
- Message Bus.** Structures the connecting middleware between applications as a communication bus that enables the applications to work together using messaging.
- Message Dispatcher.** A component that sends messages to multiple consumers.
- Message Filter.** Eliminates undesired messages, based on a set of criteria, from being transmitted over a channel to a consumer.
- Messaging Bridge.** A component that connects messaging systems and replicates messages between these systems.
- Messaging Gateway.** Encapsulates message-based calls into a single interface in order to separate it from the rest of the application code.
- Messaging Mapper.** Transforms requests into business objects for incoming messages, and reverses the process to convert business objects into response messages.
- Normalizer.** Converts or transforms data into a common interchange format when organizations use different formats.
- Point-to-point Channel.** Sends a message on a Point-to-Point Channel to ensure that only one receiver will receive a particular message.
- Polling Consumer.** A service consumer that checks the channel for messages at regular intervals.
- Process Manager.** A component that enables routing of messages through multiple steps in a workflow.
- Publish-subscribe Channel.** Creates a mechanism to send messages only to the applications that are interested in receiving the messages without knowing the identity of the receivers.
- Reliable Sessions.** End-to-end reliable transfer of messages between a source and a destination, regardless of the number or type of intermediaries that separate endpoints.
- Remote Façade. Creates a high-level unified interface to a set of operations or processes in a remote subsystem to make that subsystem easier to use, by providing a course-grained interface over fine-grained operations to minimize calls across the network.
- Replay Protection.** Enforces message idempotency by preventing an attacker from intercepting a message and executing it multiple times.
- Request Response.** A two-way message communication mechanism where the client expects to receive a response for every message sent.
- Request-Reply.** Uses separate channels to send the request and reply.
- Selective Consumer.** The service consumer uses filters to receive messages that match specific criteria.
- Service Activator.** A service that receives asynchronous requests to invoke operations in business components.
- Service Contract. A schema that defines operations that the service can perform.
- Service Interface. A programmatic interface that other systems can use to interact with the service.
- Store (REST). Allows entries to be created and updated with PUT.
- Transaction (REST). Resources that support transactional operations.
- Transactional Client.** A client that can implement transactions when interacting with a service.
- Validation.** Checks the content and values in messages to protect a service from malformed or malicious content.
The following guidelines will help you to choose an appropriate implementation technology for your service layer:
- Consider using ASP.NET Web services (ASMX) for simplicity, but only when a suitable Web server will be available.
- Consider using WCF services for advanced features and support for multiple transport protocols.
- If you are using ASMX and you require message-based security and binary data transfer, consider using Web Service Extensions (WSE).
- If you are using WCF and you want interoperability with non-WCF or non-Windows clients, consider using HTTP transport based on SOAP specifications.
- If you are using WCF and you want to support clients within an intranet, consider using the TCP protocol and binary message encoding with transport security and Windows authentication.
- If you are using WCF and you want to support WCF clients on the same machine, consider using the named pipes protocol and binary message encoding.
- If you are using WCF, consider defining service contracts that use an explicit message wrapper instead of an implicit one. This allows you to define message contracts as inputs and outputs for your operations, which then allows you to extend the data contracts included in the message contract without affecting the service contract.