Even the quasi-automatic matching of requests to specific sessions, often called session affinity, becomes an issue as you scale. If the session is provided in a web server process every request from that user for that session must go to the same web server. Alternately, the session could be stored in a shared resource, like a database, but doing so slows every operation as the session is loaded back into process by the application (the web server).
And it gets worse. Besides simply coordinating requests and data, there are issues around session lifetime management. How long should a session be valid? What should be done when a session is no longer valid? Who cleans up whatever resources the session used during its lifetime? For some older client server applications this was easy; when the application closed the session was over. This shows how tightly bound the application was to its session. In the distributed world this is a much more difficult problem to solve. Unless you’re using a single, open, persistent TCP/IP connection there is no definite lifetime for a session. How does state relate to coupling?
Finally, the sequence of steps and data exchanged in state-heavy processes creates a condition we discussed in Loose Coupling. This prescribed sequence of operations and data exchanges creates glue that makes our programs stay in a specific form. This is an implicit contract, and is especially ill-advised, because it is not expressed via the service itself, but only through outside documentation, if at all.
State makes it harder to change our services and applications. You can’t just create a new mandatory middle step without coordinating with all of your consumers. That would break their existing applications. State affects coupling – generally more increases it, less decreases it.
There are many technical reasons why state is bad, but for now let’s stick with that it’s complicated, requires a lot of resources and overhead, and creates an implicit contract.
How can you avoid state?
There are several strategies one can use to avoid state in services and they work better in specific situations. The easiest, perhaps, is to defer to the consumer. This is what classic client server applications did. The client was responsible for tracking and taking care of its own session. This works, and our shopping cart example fits this type of state deferral pattern. The web portal must keep its own shopping cart and, when the user is ready, submit it to the Order service.
Another popular technique is to defer to the ultimate destination application to which the service is a conduit. This is less desirable, though also an option. I say less desirable because a service so designed needs to carry information that really has nothing to do with the service itself–perhaps a session ID or token of some sort. If this route is necessary, using a custom SOAP header for this information rather than placing it in the request message bodies is better. The reason is twofold: first, this information has nothing to do with the operation you are providing, it is a technical detail. Second, it is likely to be needed on other operations, so creating a SOAP header for it saves developers from having to put this information into every one of their operation input messages.