Service statelessness principle

Service statelessness is a design principle that is applied within the service-orientation design paradigm, in order to design scalable services by separating them from their state data whenever possible. This results in reduction of the resources consumed by a service as the actual state data management is delegated to an external component or to an architectural extension. By reducing resource consumption, the service can handle more requests in a reliable manner.

Purpose
The interaction of any two software programs involves keeping track of the interaction-specific data as each subsequent interaction may depend upon the outcome of the previous interaction. This becomes more important in distributed architectures where the client and the server do not exist physically on the same machine. In two-tier architectures, the responsibility of tracking this interaction-specific data rested upon the rich clients, which was not an issue as each client used to reside on an individual computer. However, within n-tier architectures, the state management responsibility shifted from the client to the application or the web server. This introduced the need for some middleware state management extensions so that the server could handle multiple concurrent client requests by deferring the actual activity-specific state data to such extensions e.g. storing session data in a database in ASP .NET applications. This helps freeing up the memory resources in favor of increasing server responsiveness and the ability to entertain more client requests.

In a service composition, a service may need to store activity-specific data in memory while it is waiting for another service to complete its processing. Consequently, in case of service-orientation, an efficient management of service activity related data becomes more important as service-orientation puts a lot of emphasis on service reuse. The service not only needs to deal with managing state data, which is created as a result of interacting with a consumer program, in the context of a particular business process but also in relation to the interactions with other types of consumer programs that are part of multiple business processes. As reusability goes up, so does the overhead of managing state data. The Service Statelessness principle provides guidelines in favor of making the service stateless by shifting away the state management overhead from the services to some other external architectural component. This further helps in the overall scalability of the service-oriented solution.

Application
The correct application of service statelessness requires an understanding of the various types of state information that need to be managed.

Context data
Within a service composition, service may be required to keep track of data that is specific to the running of a particular service activity, which is usually linked with the coordination of messages, e.g. workflows, and the associated rules that govern how the rules are to be interpreted.

Business data
This is the data that relates to the actual business process, run by the current service activity e.g. customer records, etc. on some occasions this type of data may need to be temporarily stored, especially if it acts as an input to the next stage within the service activity.

Session data
This relates to the connection information between the services e.g. when consumer programs and services are communicating back and forth, some sort of correlation may be required in order to fire the subsequent request only to the particular instance of the service as only that instance knows about the previous service interaction.

Statelessness and service types
The Service Statelessness principle could be applied to varying extents in relation to the type of the solution logic enclosed by the service.

Task services
The task services contain solution logic that is specific to a particular business process and hence their reuse level is low. However, these services contain context data (workflow rules) about the service activity, which is directly proportional to the size of the service composition that is being administered by the task service. As a result, designing such services with state deferral options reduces their memory footprint and makes them more responsive.

Utility services
These kinds of services may need to be stateful in order to provide statelessness for task and entity services. On the other hand, a highly reusable utility service, e.g. a utility service that acts as a wrapper for a legacy system, needs to be moderately stateless so that it can entertain multiple concurrent requests.

Entity services
Being independent of any specific business process, these services are regarded as the most reusable services. Another important factor is that they process data related to business entities and as such require higher levels of statelessness so that they are not burdened with keeping track of business data that they may need to retain in order to provide the required functionality.

The statelessness could either be achieved by delegating state management to some shared architectural extension, e.g. a middleware product that exists outside of the service implementation boundary or to a dedicated mechanism that exists inside the service boundary e.g. a dedicated database.

Considerations
It may not always be possible to provide a dedicated state deferral option for each service as this clearly requires additional investment. On the other hand, using a shared state deferral option may create a dependency for the service, which may stand in way of service evolution.

The storage and retrieval of state information may inadvertently affect the response time of the service as both of these tasks may prove computationally intensive as first the data needs to be converted into the native format of the storage extension and vice versa when it comes to retrieving the same information.

Designing stateless services requires extra efforts and time as the service needs to contain logic that interfaces with the state deferral extensions. This in turn would require additional code and testing.