Understanding Service Statelessness

Software architecture

Statelessness refers to the storage of variable values internal to the service. If a service is truly stateless, we should be able to call any method or reference any property on the service, and as long as we pass the same parameters, the service should behave in the same way. In other words, no values are stored in the service that cannot be changed by an outside caller and that affect the outcome of an operation.
To understand statelessness, review the code below which shows a service that describes the process of buying an item at a store.
///

/// Class representing the process of buying an item
/// from a store.
///

public class SalesclerkNoRefunds
{
///

/// Method to call when a customer is buying an item.
///

public void SellItem(string itemToSell, string nameOfPurchaser)
{
var price = GetItemPrice(itemToSell);
GetPaymentFromCustomer(price);
}

///

/// Interacts with the customer to get payment.
///

private void GetPaymentFromCustomer(double amount)
{
//Get the payment from the customer
}

///

/// Lookup the price of the item and return it
///

private double GetItemPrice(string itemToSell)
{
//Lookup the price of the item and return it.
return 15.00;
}
}

In this sales process, we can purchase an item from any clerk in the store we choose, and we can complete our purchase without any problems. This service has no local variables and no state, so any sales clerk can handle the transaction. However, this process does not allow for returning an item to the store; it supports purchasing items only. Let’s enhance the class to add a method for returning an item we previously purchased. Consider the enhanced class and the ReturnItem method, as demonstrated below.
///

/// Class representing the process of buying an item.
///

public class SalesclerkWithState
{
private List _rememberedPurchases;

public SalesclerkWithState()
{
_rememberedPurchases = new List();
}

///

/// Method to call when a customer is buying an item.
///

public void SellItem(string itemToSell, string nameOfPurchaser)
{
var purchase = new Purchase();
purchase.PurchaserName = nameOfPurchaser;
purchase.SaleDate = DateTime.Today;
purchase.ItemSold = itemToSell;
purchase.AmountPaid= GetItemPrice(itemToSell);
ReceiveOrRefundPayment(purchase.AmountPaid);
_rememberedPurchases.Add(purchase);
}

///

/// Method to call when the customer is returning the item.
///

public void ReturnItem()
{
var purchase = RememberItemSoldToCustomer();
ReceiveOrRefundPayment(-purchase.AmountPaid);
}

///

/// Charge the customer credit card for the purchase.
///

private Purchase RememberItemSoldToCustomer()
{
//Recall the customer and how much they paid.
return _rememberedPurchases[0];
}

///

/// Interacts with the customer to get the credit card number.
///

private void ReceiveOrRefundPayment(double amount)
{
//Get the payment from the customer
}

///

/// Lookup the price of the item and return it.
///

private double GetItemPrice(string itemToSell)
{
//Lookup the price of the item and return it.
return 154.00;
}

private class Purchase
{
public string PurchaserName { get; set; }
public double AmountPaid { get; set; }
public DateTime SaleDate { get; set; }
public string ItemSold { get; set; }
}
}

n this class, we introduce the ReturnItem method, but notice that we also introduce state into our service. To allow a customer to return an item, the clerk must know what that customer paid for the item. If we did not do this, someone can buy an item during a sale and then return the item when the sale is over and get full price in return. Therefore, our sales clerk needs to remember every person who purchased something, what was purchased, and for what price. To track this information, we introduce the Purchase class and a list of purchases.
We can now buy and return a purchase at the same store. However, with the current design, we must always talk to the same salesclerk who originally sold the item. Notice that the Purchase class is private representing the idea that only the original salesclerk can access his own memory. If we go to a different salesclerk, he will not remember the purchase and not be able to refund the money. This is the penalty of introducing state into a service. We cannot instantiate two copies of the Salesclerk class and return something randomly between the two. We must couple ourselves to the salesclerk that originally sold us the item.
So far, the only two options in building the service are as follows:
• To be stateless but not contain all the functionality we wish
• To introduce application state and the problems associated with it
We can’t get rid of the need for saving the state of the system, but we can solve this problem by having the state stored externally and handing it to the service when necessary. Consider a stateless version of the sales process with refunds, as shown below.
///

/// Class representing the process of buying a shirt.
///

public class SalesClerkWithNoState
{
///

/// Method to call when a customer is buying an item.
///

public SalesReceipt SellItem(string itemToSell)
{
var receipt = new SalesReceipt();
receipt.SaleDate = DateTime.Today;
receipt.ItemSold = itemToSell;
receipt.AmountPaid= GetItemPrice(itemToSell);
ReceiveorRefundPayment(receipt.AmountPaid);
return receipt;
}

///

/// Method to call when the customer is returning the item.
///

public void ReturnItem(SalesReceipt receipt)
{
GetReceiptAndItemFromCustomer();
ReceiveorRefundPayment(-receipt.AmountPaid);
}

///

/// Get the necessary items from the customer.
///

private void GetReceiptAndItemFromCustomer()
{
//receive the item and receipt from customer
}

///

/// Interacts with the customer to get the credit card number.
///

private void ReceiveorRefundPayment(double amount)
{
//Get the payment from the customer
}

///

/// Lookup the price of the item and return it.
///

private double GetItemPrice(string itemToSell)
{
//lookup the price of the item and return it
return 154.00;
}
}

///

/// Reflects information about the purchase.
///

public class SalesReceipt
{
public double AmountPaid { get; set; }
public DateTime SaleDate { get; set; }
public string ItemSold { get; set; }
}

In this class, we introduce the SalesReceipt class and remove the Purchase class. We made the SalesReceipt class public. We also remove the private list of purchases that we once needed to remember all of the purchases.
By introducing the public idea of a receipt, we transfer the burden of remembering state to the outside caller, making the SalesClerkWithNoState conform to the stateless requirement. With the receipt in hand, we are now free to visit any sales clerk we want to return the item, making the transaction much easier to complete and the service much more useful.
We strive to make services as stateless as possible, but quite often, it is necessary to maintain some state in the service. How much state is maintained is dictated by the purpose of the service.

References:
Reengineering .NET
By: Bradley Irby