Beyond the State: Redefining Application Truth Most developers default to treating current state—like an account balance—as the ultimate source of truth. It is a mess to manage when requirements scale. If you rely solely on state, implementing a transaction history or complex undo/redo logic becomes a redundant nightmare. You end up maintaining a list of past actions alongside the current state, and the moment they drift apart, your system loses integrity. By shifting the **ground truth** from the state to the history of transactions, you fundamentally change how the application breathes. In this model, the state is no longer a fixed value; it is a derived result. This is not just a semantic change. It transforms your Command Pattern implementation from a set of instructions into a permanent ledger. Prerequisites and Tools To follow this tutorial, you should be comfortable with Python and basic object-oriented design. We utilize the following tools: - **Python 3.10+**: For data classes and type hinting. - **GitHub Repository**: The example code provides the starting point for these modifications. - **Protocols**: Used to define the structural interface for our command objects. Refactoring for a Transactional Ledger To implement this, we first modify the BankController. Instead of executing commands immediately, we register them in a list called a ledger. We also replace complex undo/redo stacks with a single `current` index pointer. ```python @dataclass class BankController: ledger: list[Transaction] = field(default_factory=list) current: int = 0 def register(self, transaction: Transaction): # Delete any "future" redo history after current pointer del self.ledger[self.current:] self.ledger.append(transaction) self.current += 1 def undo(self): if self.current > 0: self.current -= 1 def redo(self): if self.current < len(self.ledger): self.current += 1 ``` This architecture makes undoing an action as simple as moving an integer pointer backward. No state is actually reversed; we simply decide which transactions to ignore during the next calculation. Computing State as a Cache Since the balance is no longer the ground truth, we treat it as a `balance_cache`. This signals to other developers that this value is volatile and can be reconstructed at any time. To find the current balance, we iterate through the ledger up to the `current` pointer. ```python def compute_balances(self): # First, reset all account caches to zero self.bank.clear_all_caches() for transaction in self.ledger[:self.current]: transaction.execute() ``` Practical Applications This pattern isn't limited to banking. **Non-destructive editing** tools like Final Cut Pro use this extensively. They never modify your original video file; they store a series of transformations (commands) and render the result as a "cache." Similarly, 3D modeling tools apply effects on top of base meshes, allowing you to toggle modifications without ever losing the original data. Syntax Notes and Best Practices 1. **Method Renaming**: Change `execute` to `register` in the controller to reflect that the command is being logged, not necessarily immediate state-altering. 2. **Pointer Logic**: Always clear the "future" part of the ledger when a new transaction is registered after an undo. Failing to do so creates a branched history that the simple pointer cannot resolve. 3. **Performance**: For systems with thousands of transactions, implement "snapshot" points where you store the computed balance at a specific index to avoid re-calculating the entire history every time.
2021-command-transactions
Products
Nov 2021 • 1 videos
High activity month for 2021-command-transactions. ArjanCodes among the most active voices, with 1 videos across 1 sources.
Nov 2021
- Nov 19, 2021