State Transition Command

Software developers can reason through any technical system as a sequence of transitions between states. If systems architects plan their designed system around explicit series of state changes triggered by events, they will classify it as one that fol…

Software developers can reason through any technical system as a sequence of transitions between states. If systems architects plan their designed system around explicit series of state changes triggered by events, they will classify it as one that follows the principles of the event-driven architecture. A developer can apply such architecture on different project scopes without involving pipelines to distribute messages across a system.

Arguably, almost anything, tangible or not, might have an associated state and rules for state transitions — one could think of this operation as a way of moving a system from a particular state to the other. Today I would like to discuss the possibility of splitting a state transition into two distinct parts — a state transition command builder and a state transition command executor, and the pros and cons of such an approach. I represented the ideal state transition flow down below in a form of a diagram:

A diagram representing the ideal flow of a state transition. Created using PlantUML.

State Transition

In order to have any state transitions to occur, the system in question needs to accept an input, in a form of a event or command (regardless of the transportation layer used to deliver it into the system). There exists a fundamental difference between the two, as an event notes that something happened whilst a command forms a request of something to happen. If something indeed happened, the system must (eventually) conform to that fact but, on the other hand, it can reject any request.

Each input should have an explicit domain (in the DDD sense) associated with it. Simply put, in an online video game, requesting a change of the avatar of a user usually does not involve updating their visited locations. Reducing the input domain to a reasonable minimum allows for fast calculations of the current and the desired states within that domain.

Figuring out the current state might involve multiple calls to various databases, but it may as well require no action at all, especially when having the current state as an empty set by definition — changing the user’s avatar serves as a perfect example, as the current avatar provides no valuable information for the change. I find it crucial to understand that getting the current state of any system usually does not happen instantaneously, which means that some information within the result expires after fetching — the system should account for that by not relying on the current state more than necessary. Some software developers refer to the calculation of the current state as gathering facts about the system, especially in infrastructural projects.

The calculation of the planned state always involves the input, but it does not necessarily need to involve the current state. For instance, if a user received enough experience points to advance to a new level, and the state contains an information about that level, this calculation must happen before the creation of the desired state. An avid reader might realise though that the system could calculate the level on demand, using the experience points, removing it from the state and passing only the number of these points onto the planned state.

I consider a state transition component as part of the system which knows about the current and the planned state and has the capacity to eventually move the system from one state to the other. The aforesaid moving relies on comparing certain attributes between the states and performing required actions, both operations often intertwined, as shown in the Simple State Transition Flow diagram beneath this paragraph. Providing clear separation between the two might could bring a lot of benefits to the usability and maintainability of any system.

An example of a simple state transition flow. Created using PlantUML.

State Transition Command

Instead of building the aforementioned component, one could describe the transition in a form of a command object. Such a command would consist of either ordered or unordered set of instructions that its executor could apply to the system without knowing anything about the initial input and the previous and the desired states. An important realisation should transpire — the command calculation can happen in one subsystem and the execution in another.

State Transition Command Builder

I will utilise an example to show how to construct a command. Creating an officially recognised party in a game might require sending the following command from a game client to one of many game servers (I assume the request sender acts as the leader of the new party):

As a rule of thumb, any receiving server should sanitise every incoming piece of data. I will not delve much into this topic here, but the command has to conform to the agreed format and no party name can exceed an arbitrarily decided number of characters, for instance — thirty two. Game clients, in any version, must conform to the same limitations as game servers do.

Party creation has an associated domain in the DDD sense — parties and party membership of the request sender. The rules within the domain state that there cannot exist two parties that share the same name, thus the check for name uniqueness must happen either during the current state creation or during the execution the state transition command. For the purpose of this article I decided to check it when running the first phase.

The current state for a new party, on the condition that the database has no other party with the same name, shall contain no attributes about the party itself (as it does not exist yet) but it will hold information on a potential party that the user has membership of. The user cannot belong to two parties at the same time, and the command executor has to remove the association to the previous party, if one exists. Therefore, I could represent the party-creation state with the structure presented underneath:

I intend to use the same structure to illustrate the desired state. It should contain the ID of the new party (the builder could use a UUID generator to obtain it), the party’s name, and a rather obvious obligatory association between the user and the newly-created party. I would like to point out that the builder can create this structure without knowing the current state.

Constructing the state transition command boils down to performing an equivalent of a set difference between the planned and the current state. In my case, the builder should calculate which party to create, which association to remove (if it exists in the first place) and which association to make. I wrote an exemplary command type down below:

Please note that the state transition command has little in common with the original command coming from the request, as they operate on different layers of the system — the former on the technical layers and the latter on the business layer. As the command has no runtime references, the builder could serialize it and send it to other subsystems capable of processing it, if necessary. As I hinted before, postponing immediate state transitions might allow certain distributed systems to assign correct priorities for different transitions and could reduce the code complexity by offloading specific mutating operations to different microservices.

State Transition Command Executor

The command executor expects to receive a command and apply only the instructions contained within it. Its particular implementation always depends on a specific case, taking into account databases (SQL and NoSQL alike), message queues, event streaming and caches used in the system in question. To describe an exemplary implementation, I will continue with the party-creation case I wrote earlier in this article.

If my model system maintains its users and parties using a single SQL database, I can assume three operations need to happen to execute a party-creation command:

  • creation of a new party by the SQL INSERT statement,
  • removal of the previous user-to-party association (if it exists) utilising the SQL DELETE statement,
  • adding a new user to party association leveraging again the SQL INSERT statement.

Please note that the command itself has no notion of any SQL concepts, including the underlying database layout — this comes about not by accident, as outlining a list of instructions tells nothing of precise delivering methods. Commands require technical language to certain extent but relying on specific technology stacks within them would vendor-lock the entire system in question.

One could think of other operations that could occur, for instance, notifying the user of the party creation. I believe that such requirements must exist first within the definition of a command, as the executor should rely only on the guidelines written down in the command object. Again, if the system should notify the user, the command ought to contain the information on the notification without telling the executor which technology to leverage to perform such a task.

If the executor lives in a different subsystem than the command builder, the command might need to specify where the executor should send the execution result to, if applicable. In its simplest form, I would define a task result as either success or failure, whilst in certain cases, I could recognise a need for emitting a partial success information back to the user. If the system designers believe that the executor should retry the command execution in case of failure, they ought to adjust the command object accordingly.

Summary

Reducing systems to sequences of state transitions allows system builders to explicitly define how aforementioned transitions ought to transpire. Having one subsystem create a state transition command and making another execute it brings separation of concerns in regard to what should happen (the former) and how it should happen (the latter). Using such a pattern even for small systems might prove beneficial in terms of reduction of potential future maintenance labour costs as learning about it enables some developers to write more organized code.


State Transition Command was originally published in Level Up Coding on Medium, where people are continuing the conversation by highlighting and responding to this story.


Print Share Comment Cite Upload Translate
APA
Gregory Pabian | Sciencx (2024-03-28T09:29:46+00:00) » State Transition Command. Retrieved from https://www.scien.cx/2021/03/02/state-transition-command/.
MLA
" » State Transition Command." Gregory Pabian | Sciencx - Tuesday March 2, 2021, https://www.scien.cx/2021/03/02/state-transition-command/
HARVARD
Gregory Pabian | Sciencx Tuesday March 2, 2021 » State Transition Command., viewed 2024-03-28T09:29:46+00:00,<https://www.scien.cx/2021/03/02/state-transition-command/>
VANCOUVER
Gregory Pabian | Sciencx - » State Transition Command. [Internet]. [Accessed 2024-03-28T09:29:46+00:00]. Available from: https://www.scien.cx/2021/03/02/state-transition-command/
CHICAGO
" » State Transition Command." Gregory Pabian | Sciencx - Accessed 2024-03-28T09:29:46+00:00. https://www.scien.cx/2021/03/02/state-transition-command/
IEEE
" » State Transition Command." Gregory Pabian | Sciencx [Online]. Available: https://www.scien.cx/2021/03/02/state-transition-command/. [Accessed: 2024-03-28T09:29:46+00:00]
rf:citation
» State Transition Command | Gregory Pabian | Sciencx | https://www.scien.cx/2021/03/02/state-transition-command/ | 2024-03-28T09:29:46+00:00
https://github.com/addpipe/simple-recorderjs-demo