Banking System
Atomic transfers, validation, and idempotency
An idempotency record contains both request identity and the original response. Replaying by recomputing current balances is wrong: later deposits or transfers may have changed them. Store a successful request's exact response and reject conflicting reuse of its identifier.
using Identity=std::tuple<std::string,std::string,Int>; // source,target,amount
// Validate amount and identifiers before consulting the replay registry.A failed transfer consumes neither money nor its request ID, allowing a later retry after funding. Debit, credit, outgoing ranking, and replay storage are one logical transition. Validate all preconditions before mutating balances. Summing balances before and after a transfer exposes accidental money creation, while checking outgoing totals catches mistakenly ranking deposits as spending. The exercise assumes serialized calls and bounded arithmetic; a durable bank would need a transaction spanning all affected rows.
C++ contract conventions: numeric values use signed Int (long long); all inputs and intermediate arithmetic are within its representable range. Opt<T> means std::optional<T> and absence is std::nullopt; Vec<T> means std::vector<T>. Invalid documented arguments throw std::invalid_argument. Inputs passed by const reference remain caller owned. Returned containers and records own their contents. Calls use the declared C++ parameter types. Implicit conversions performed by the caller are outside the runtime validation contract. Unless a lesson states otherwise, string ordering is lexicographic over Unicode code points (Text) or UTF-8 identifiers (std::string).
Put it into practice
Implement the exercise in a full-screen workspace, then run its tests.