Three internal domains defined - Guarantee, Action, Reference.
Incoming message is validated by ECDSA decryption of signer ID. JSON schemas enforce a language-agnostic definitions for 90 to 95% of validation rules. Code functions can validate the remainder.
Guarantee uses AWS SQS queues to guarantee state and execution. Stores the ECDSA message in its own queue named Msg_ID. This is the only place the original message exists until it is archived.
The Action Lambda function accepts Msg_ID and adds it to the feeder queue. The controller pulls queue entries, retrieves and passes the current state into State Machine which executes the next action.
The action pulls the original message, executes, then updates Msg_State. The Msg_ID is re-submitted to the feeder queue for the next iteration until End_State is reached.
The Reference Lambda receives a subset of message attributes for reporting/tracking purposes, such as creation_date, client_id, etc. The original message is immutable except for Msg_State. The controller updates Reference with current state before each iteration.
Complexity is mostly isolated in the State Machine/Rule Engine, so most future changes are there. The rest of the system should be stable, needing few changes except the addition of new actions.
My original design should have had an entry API to issue a unique Msg_ID. This is the initial client call and the client adds it to the transaction message before it's signed, making it part of the immutable message. The Msg_ID's timestamp has an associated time window to accept the message.
Other peripheral areas are covered in my AWS primer.