BACKGROUND IMAGE: stock.adobe.com
In a world of microservices, as well as functions and serverless, there is arguably no concept more pertinent than application state management. The lack of understanding of statefulness and statelessness may stem from the problem that the term state often has a wide spectrum of definitions associated with it that range from purist to practical. Application architects must consider the best approach to state management for the given scenario.
The notion of state indicates that information is processed in context, meaning that there is an expected sequence of happenings and the way in which the application handles one step in the sequence depends in part on the sequence overall.
An electronic payment is an example of natural state in action. In step one, the user logs on to her account. She reviews her balance and minimum payment in step two. Step three is when the user makes a payment. In step four, she reviews her new balance. And in the final step, she logs out. It's obvious in the application design that the data that the user sends to the application must get interpreted in this sequential context.
Problems arise in applications either because developers don't handle a given natural state properly or because they accidentally introduce stateful behavior that isn't natural or necessary.
To be stateful, the application must store information, either in the app or via an external system. If an application stores information internally, then changes to that information can alter how the application handles subsequent activities. Storing information within a monolithic application is common, but that application state management practice is problematic for microservices. Microservices architectures are designed to replace a failed component or scale up a component without any consequences; stored information does not carry over to replacement or replicated instances.
Stateful behavior can cause problems related to resiliency and scalability, and the answer is not to store information in an application component. Serverless computing -- also called functional and lambda computing -- includes a rule that the output of a given software component has to depend only on the inputs. Therefore, the component can store no data between uses, nor can data be introduced from the outside, with the exception of the data that passed to the component when it was invoked. The abstract formula x = f(y) -- read as "x is a function of y" and hence the name functional -- means that the same value of x will always produce the same value for y, because nothing else can be considered in the processing.
Functional components are never stateful, but they're often not particularly helpful either because of the challenge of natural state present in most applications. Something, somewhere must be stateful in the real world of transaction processing. To manage application state, architects must introduce it gracefully. There are two proven approaches: the front-end approach and the back-end database approach.
Distinguish between state control approaches
Front-end state management is based on the assumption that, if people are doing something that requires context, state should be maintained where the user is. When a web browser, smartphone app or another UI takes a user through a dialog that requires multiple steps, state is necessary. The natural state can pass through from the originating component. If any of the components that are invoked during the processing of a specific transaction returns its intermediate data to the component that invoked it, you can create a chain that feeds all intermediate results back to the source, which can submit it with the data for the next natural step. As long as the source doesn't lose track of things, the data is available for the application to work.
Back-end state management is the opposite. To save results between the natural steps in processing, the application components write the data to a database, tagging it with a unique transaction ID. At the next natural step in the sequence, the application reads the back-end state data for the transaction and restores the information it needs. In an application composed of multiple components that all scale and redeploy independently, any instance of a given component can use the back-end database to acquire its stateful data. The back-end state control prevents a loss of natural state.
These options to harmonize the need for stateful data and to avoid stateful components include using front-end or source storage of stateful data, using back-end state control or not storing anything at all. The best option depends on the application.
Application state management in web-centric uses of microservices is probably most easily based on front-end mechanisms. Include procedures that gracefully recover if the user abandons a transaction/sequence or disconnects. Also, beware that a disconnected user does not leave a stateful process open for another user to inherit.
Transaction handling at the back end fits back-end state control, and stateless functions should be used anywhere that services/processes can be defined to truly not depend on stored data. The problem with back-end state control is the same as with distributed transaction processing: The designer must manage the possibility that two different instances of a process will attempt to access the same state database.
The store-nothing approach is the safest; a microservice written as a function will never have a problem with state control, no matter how it scales or redeploys, and there's no partial process threads left hanging to be picked up in error. However, as long as applications are written for human users, microservices will have to operate within the context of the natural state.