Note that the information on this page is the BETA 1 of a guide that is now released. See http://www.codeplex.com/AppArchGuide for the latest PDF and HTML content.

Chapter 15 – Service Layer Guidelines

- J.D. Meier, Alex Homer, David Hill, Jason Taylor, Prashant Bansode, Lonnie Wall, Rob Boucher Jr, Akshay Bogawat

Overview

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. The following diagram shows where a service layer fits in the overall design of your application.

ServiceLayer.PNG

Service Layer 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.

Approach

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 is defined, the next step is to design the service implementation; which is used to translate data contracts into business entities and 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.

Design Considerations

When designing the service layer, there are many factors that you should consider. Many of the 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.
  • 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 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 and how they plan to use the service you provide.
  • Design only for 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 are 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 arrive out of order, implement a design that will store messages and then process them in the correct order.
  • Contract-first Design. <<TBD>>
  • Versioning of Contracts. A new version for service contracts mean new operations exposed by the service whereas for data contracts it means new schema type definitions being added.

Services Layer Frame

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.
Data Consistency Failing to check for data consistency.
Improper handling of transactions in a disconnected model.
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 There is limited schema support.
Current tools for REST are primitive.
Using hypertext to manage state.
SOAP Not choosing the appropriate security model.
Not planning for fault conditions.
Using complex types in the message schema.

Authentication

Designing an effective authentication strategy for your service layer is important for the security and reliability of your application. Failing to design a good authentication strategy can leave your application vulnerable to spoofing attacks, dictionary attacks, session hijacking, and other types of attack.

When designing an authentication strategy, consider following guidelines:
  • 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 SSL are used with basic authentication, or when credentials are passed as plain text.
  • Use secure mechanisms such as WS Security with SOAP messages.

Authorization

Designing an effective authorization strategy for your service layer is important for the security and reliability of your application. Failing to design a good authorization strategy can leave your application vulnerable to information disclosure, data tampering, and elevation of privileges.

When designing an authorization strategy, consider following guidelines:
  • 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 using declarative principle permission demands.
  • Execute services under the most restrictive account that is appropriate.

Communication

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 TCP for more efficient communications. If the service will be deployed in to a public facing network, you should choose the HTTP protocol.

When designing a communication strategy, consider following guidelines:
  • Use dynamic URL behavior with configured endpoints for maximum flexibility.
  • Validate endpoint addresses in messages.
  • Determine your transport protocol.
  • Determine how to handle asynchronous calls.
  • Decide if the message communication needs to be on-way or two-way.
  • Determine how to handle communication reliability.
  • Determine how to handle idempotency and commutativity.

Data Consistency

Designing for data consistency is critical to the stability and integrity of your service implementation. Failing to validate the consistency of data received by the service can lead to invalid data being inserted into the data store, unexpected exceptions, and security breaches. As a result, you should always include data consistency checks when implementing a service.

When designing for data consistency, consider following guidelines:
  • Validate all parameters passed to the service components.
  • Check input for dangerous or malicious content.
  • Determine your signing, encryption and encoding strategies.
  • Use an XML schema to validate incoming SOAP messages.

Exception Management

Designing an effective exception management strategy for your service layer is important for the security and reliability of your application. Failing to do so can make your application vulnerable to denial of service (DoS) attacks, and may also allow it to reveal sensitive and critical information.
Raising and handling exceptions is an expensive operation, and it is important for the design to take into account the 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.

When designing an exception management strategy, consider following guidelines:
  • Do not use exceptions to control business logic.
  • Design a strategy for handling unhandled exceptions.
  • Design an appropriate exception logging strategy.
  • Design an appropriate notification strategy for critical errors and exceptions
  • Do not reveal sensitive information in exception messages and 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.

Messaging Channels

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 WCF. You must understand which patterns your chosen infrastructure supports, and determine the appropriate channel for interaction with consumers of the service.

When designing message channels, consider following guidelines:
  • Determine appropriate patterns for messaging channels.
  • Determine how to intercept and inspect the data between endpoints.

Message Construction

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 type 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.

When designing a message construction strategy, consider following guidelines:
  • 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.

Message Endpoint

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.

When designing message endpoints, consider following guidelines:
  • 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 disconnected scenarios. For instance, you may need to support guaranteed delivery.
  • Design for invalid messages. You should never assume that all received messages are valid.

Message Protection

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 from tampering.

When designing message protection, consider following guidelines:
  • If interactions between the service and the consumer are not routed through other services, you can use just transport layer security 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.
  • Use encryption to protect sensitive data in messages.
  • Use digital signatures to protect messages and parameters from tampering.

Message Routing

A message router is used to decouple a service consumer from the service implementation. There are three main types of routers 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.

When designing message routing, consider following guidelines:
  • Determine relevant patterns for message routing.
  • If sequential messages are sent from a consumer, the router must ensure 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.

Message Transformation

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.

When designing message transformation, consider following guidelines:
  • Determine relevant patterns for message transformation.
  • Use metadata to define the message format.
  • Consider using an external repository to store the metadata.

Representational State Transfer (REST)

Representational state transfer (REST) represents an architecture style for distributed systems. It is designed to reduce complexity by dividing a system into resources. The operations supported by a resource represent the functionality provided by a service that uses REST.

When designing REST resources, consider following guidelines:
  • Identify and categorize resources that will be available to clients.
  • Choose an approach for resource representation.
  • Decide if multiple views should be supported for different resources.

Service Interface

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 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.

When designing a service interface, consider following guidelines:
  • Use a coarse-grained interface that minimizes the number of calls required to achieve a specific result.
  • Be prepared to implement more than one interface to provide different views of the service to different clients.
  • Design interfaces to allow loose coupling with other layers of the application.
  • Design services interfaces in such a way that changes to the business logic do not affect the interface.
  • Consider implementing multiple service interfaces if the business logic may be accessed in different ways by different clients.
  • Consider implement caching, mapping, and type conversion in the interface if required by the component.
  • Do not implement business rules in a service interface.
  • Use standard formats for parameters to enable maximum compatibility with different clients.
  • Design service interfaces for maximum interoperability with other platforms and services.
  • Choose an appropriate technology for implementing your service.
  • Choose appropriate transport protocol, message formats, and security features for your service.

SOAP

SOAP is a message-based protocol 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.

When designing SOAP messages, consider following guidelines:
  • 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.

Deployment Considerations

When deploying a service layer, a software architect must consider the performance and security issues inherent within the production environment.

When deploying the service layer, consider following guidelines:
  • Locate the service layer on the same tier as the business layer to improve application performance.
  • If the service is only accessed by other applications within a local network, consider using TCP for communications.
  • Configure the service host to use transport layer security only if consumers have direct access to the service without passing through other servers or services.

Pattern Map

Category Relevant Patterns
Authentication Brokered Authentication
Direct Authentication
Federated Authentication (Single-Sign-On or SSO)
Authorization Claims Based Authorization
Impersonation and Delegation
Resource Based Authorization
Role Based Authorization
Trusted subsystem
Communication Duplex
Fire and Forget
Reliable Sessions
Request Response
Data Consistency Atomic Transactions
Cross-service Transactions
Long running transactions
Exception Handling Exception Shielding
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
Transaction
Store
Service Interface Remote Façade
SOAP Data Contracts
Fault Contracts
Service Contracts

Key Patterns

  • Service Contracts – Schemas that define operations that the service can perform.
  • Service Interface – represents the external contracts that consumers will use to interact with the service.
  • Façade – Provides a coarse grained interface to a set of operations to simply the calls.

Technology Considerations

The following technical considerations should be considered when designing a service layer:
  • Use Windows Communication Foundation (WCF) Web services where possible for maximum feature availability and interoperability.
  • Use ASP.NET Web services (ASMX) for simplicity, and only when a suitable Web server will be available

Additional Resources

For more information on service interface layer design, message design, and versioning see the following sections of the Web Service Software Factory: Modeling Edition guidance:

Last edited Dec 16, 2008 at 8:18 AM by rboucher, version 3

Comments

No comments yet.