Design Pattern
dry-operation implements a pattern that closely resembles monadic composition, particularly the Result
monad, and the Railway Oriented Programming pattern. Understanding these monadic concepts can provide deeper insight into how dry-operation works and why it's designed this way.
Monadic composition
In functional programming, a monad is a structure that represents computations defined as sequences of steps. A key feature of monads is their ability to chain operations, with each operation depending on the result of the previous one.
dry-operation emulates this monadic behavior through its #step
method and the overall structure of operations.
In monadic terms, the #step
method in Dry::Operation
acts similarly to the bind
operation:
- It takes a computation that may succeed or fail (returning
Success
orFailure
). - If the computation succeeds, it extracts the value and passes it to the next step.
- If the computation fails, it short-circuits the entire operation, skipping subsequent steps.
This behavior allows for clean composition of operations while handling potential failures at each step.
By expressing this behaviour via #step
, dry-operation lets you intermingle ordinary Ruby code in between steps as required.
Railway Oriented Programming
The design of dry-operation closely follows the concept of Railway Oriented Programming, a way of structuring code that's especially useful for dealing with a series of operations that may fail.
In this model:
- The "happy path" (all operations succeed) is one track of the railway.
- The "failure path" (any operation fails) is another track.
Each step is like a switch on the railway, potentially diverting from the success track to the failure track.
dry-operation implements this pattern by allowing the success case to continue down the method, while immediately returning any failure, effectively "switching tracks".