I have a set of tasks I perform to complete some larger operation. These tasks must be executed in linear order, and you cannot proceed onto the next task until the previous completes. For the most part, these tasks chain to each other and have no dependencies on state outside of the "chain", other than perhaps transferring state between the tasks as needed.
As an example, what I'm doing right now involves communication to a remote server to provision an encryption key onto a device. This device is responsible for generating data in a secure fashion, but the key it uses to encrypt that data is not initially known. So the server provisioning is to get that key and inject it into the device. The basic flow is this:
- Software checks if a key is present on the device, if not, it initiates the provisioning process.
- The first task is Authorization:
- Obtain an authorization token from the device
- Pass that authorization token to a server asynchronously
- Receive the response to that request from the server, and pass that response on to Task #2
- The second task is Key Injection:
- Build data required to request a key by passing the response from step 1 to the device.
- Device gives us a token which is used to call back to the server to request a new key. This is asynchronous
- Receive the response, which contains the key, and inject the key into the device.
- The third and final task is to verify the injection:
- Request key information from the device
- Send a request to the server, with this information, and the server verifies the key injection is valid. This request is asynchronous.
- Server responds, saying key injection is good, and the operation is completed.
So far, I have implemented this as a state machine like so:
Idle -> Authorize -> Inject -> Verify -> Done
And internally, the states transition to each other. For example, when the asynchronous response is received from the server while in the Authorize state, it transitions to the Inject state.
I'm doing all of this in C++, and as far as state machine libraries go, there's not many good ones to pick from. At the moment I'm using Boost.Statechart, which is really weird to use especially since there's a period of time when it isn't valid to transition from one state to the next. As an example, until the response from the server is received, and while in the Authorize state, you can't transition at all yet.
So from a software design perspective, am I choosing the right design pattern here (Finite State Machine)? If yes, should I opt for more granular states? If no, what would be the ideal pattern here?
There's no real good way to pass information from one state to the next, so I end up having to store temporary state in the state machine object itself. My nitpick about this is that that state is not useful globally; it's only useful for a moment when you begin the next task. Could be a Boost.Statechart limitation, or maybe an indication I'm choosing the wrong pattern for the job. Just not sure.