Maintaining state in an Indigo service
I've been working on usability studies for Indigo for the last 12 months now and have been learning a lot. I just learned another very valuable lesson this week, thanks to Steve Swartz.
During a study I was running last week, participants worked with a service that maintained state between calls for each session but that did not share the same state across multiple sessions. However, participants expected that the state would be persisted across sessions such that one client proxy could call an operation that would change the state and another proxy would see that state change in a different operation. Participants were writing code to bind the results of the operation to Avalon UI and the fact that state was not persisted across sessions caused them to have to rewrite a lot of their data binding code.
The contract for the service was as follows:
[ServiceContract(Namespace = "https://Usability.Task")]
interface IAlbumService
{
[OperationContract]
Album[] GetAlbumList();
[OperationContract]
void AddAlbum(string title);
[OperationContract]
int GetNumberOfAlbums();
[OperationContract]
void SellAlbum(string title);
}
You'll notice that the contract says nothing about whether or not state is persisted. I had been setting the InstanceMode property on the service implementation to maintain state but of course, this is part of the implementation, not the contract, and so consumers of the service have no visibility into the setting of this property. I had thought that it might be useful to communicate to users the setting of the InstanceMode property but Steve made it clear that this was not appropriate, since this really has nothing to do with the statefulness of the service. This property is provided as a way to allow service developers to deal with perf and scalability issues in their service implementation, not necessarily to provide service developers with a mechanism by which to preserve state between calls or between sessions.
So I asked how best to communicate the stateful nature of a contract? Steve offered two suggestions (hopefully Steve doesn't mind me quoting him below). The first involved adding Open and Close methods to the contract:
"I would add an Open() and Close() method to the contract. Open() would be the only method that could initiate a connection; Close would be the method that would always close a connection and spill the instance on the service. Open() would take a key that would identify the app session being dealt with. The service would be storing data in a database (or in a file – whatever works for the particular app)."
The second involves passing an id in each of the operations to identify the 'session' that the client is participating in:
"Many people would argue that a stateless “per-call” service is more scalable and robust. The way to build that into the contract is to put an album-list identifier into each of these methods. If any particular client was only going to have one album list, then you might use cookies so that all calls from machine X would go back to the same album list. These strategies scale better because they don’t rely on connections. You might use these strategies even with per-session instances, because they would let the client app use a session for three or four calls in a particular part of the UI (caching security information in the channel), and then let them get at the same state from some other session."
In both suggestions, the fact that state is persisted in some way is made explicit by the design of the contract. In terms of usability, being explicit is almost always a good thing. It means that there is one less assumption that users have to make about how an API works and in the case of the usability study last week, would likely have prevented participants from rewriting all their data binding code after realizing that the initial assumptions they made about how the service operated were incorrect.
I think that one of the reasons that I hadn't designed the contract this way originally was because I was still thinking in an object oriented fashion instead of a service oriented fashion. I was still thinking in terms of objects that have state instead of services that are stateless. Looking back now it seems like such an obvious mistake to make but I expect that it's one that many people might make when they transition from building objects to building services. It strikes me that being explicit about maintaining state in the design of the service contract could be a best practice that might be codified in tools such as FxCop and others. Having a rule that fires whenever it appears that a service implementation is storing state and does not expose Open or Close operations or does not appear to take some id or token in each operation might be a useful way to guide developers towards best practices and help them think in terms of services. Or even better might be some tool that helps generate a contract for you based on certain requirements that the user feeds into the tool and thus would generate the appropriate operations for you.
I'm not sure how feasible it would be to write such an FxCop rule or build such a tool but the idea of packaging up best practice guidance such as this in tools is appealing. I'd be interested in hearing about other best practices that people follow when building services and to learn about ways in which people ensure that those best practices are followed.
Comments
- Anonymous
August 30, 2005
Isn´t it one of the major principles of soa, that services should always be stateless? Therefore: different calls of a service operation should not depend on each other. If there´s state (and I think there´s always some kind of state) the database is the right place for it. The interface of the service operation should care that it contains all the parameters that are necessary to access the state. If there´s a situation where different operations depend on a common session state, try to encapsulate them in a (coarser grained) service. - Anonymous
September 04, 2005
Also (in addition to Harry's comment)
isn't AddAlbum and (event more so)GetNumberOfAlbums too chatty for a Service interface? - Anonymous
September 04, 2005
Having state or not having state is more of an implementation decision than a contract decision.
A program that consumes a service of some kind need not to assume anything about the service except the contract the use to communicate.
The sample of sending some kind of an identifier of the session to a web service can be a method that when implemented in the service can actually have state behind it of some kind. Perhaps this state will persist to a database before the method invokation returns and upon the next call (which can get to a different service) will retrieve the state and continue from there on.
You see, this is all about implementation and there is no need for the application consuming the service to assume anything but the contract.
Of course, this doesn't solve the problem of how to build a contract in a right manner that will enable you to switch (if necessary) between a stateful service (of any kind) and a stateless one.