Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Service Objects with Dry.rb: Monads and Transac...

psadauskas
November 10, 2021

Service Objects with Dry.rb: Monads and Transactions

Service objects are an important tool in your toolbox, and Dry.rb's Transaction library is one of the most powerful, and one of the most magic. It's a "business transaction" DSL, and has error handling as a primary concern. We'll start by exploring Monads in Ruby (they're not scary!). Then we'll see how that simple concept unlocks another level of service objects that are far more robust and testable, and how to wire them all together with Dry::Transaction. Finally we'll touch on extending transactions with custom steps, and how to integrate them into an existing application.

psadauskas

November 10, 2021
Tweet

Other Decks in Programming

Transcript

  1. Hi! Paul Sadauskas Lead Architect at TextUs • github.com/paul •

    @theamazingrando Like everyone else, we’re hiring! https://textus.com/jobs This is the talk that made me want to join TextUs — Jason Taylor Ruby on Rails Podcast Ep 389, Oct 27 2021
  2. Service Objects • Adapter • Command • Decorator • Query

    Object • View Model • Presenter • Form Object https://refactoring.guru
  3. Dry::Transaction Dry::Transaction is a business transaction DSL. It provides a

    simple way to define a complex business transaction that includes processing over many steps and by many different objects. It makes error handling a primary concern by taking a “Railway Oriented Programming” approach to capturing and returning errors from any step in the transaction. • A business transaction is a series of operations where any can fail and stop the processing. • A business transaction may resolve its operations using an external container. • A business transaction can describe its steps on an abstract level without being coupled to any details about how individual operations work. • A business transaction doesn’t have any state. • Each operation shouldn’t accumulate state, instead it should receive an input and return an output without causing any side-effects. • The only interface of an operation is #call(input). • Each operation provides a meaningful piece of functionality and can be reused. • Errors in any operation should be easily caught and handled as part of the normal application flow. — From the Dry-Transaction documentation
  4. Anatomy of a Transaction • Each “step” is run in

    the order declared in the `step` DSL • Each step returns a “Result”, which is “Success” or “Failure” • If Success, it gets passed to the next step • If Failure, execution halts, no more steps are run, and that Failure is returned.
  5. Maybe Monad A wrapper than can contain only two possible

    items: • Just/Some - wraps the value • Nothing/None - when the value is missing Monad Presence Absence Haskell/Elm Maybe Just Nothing Rust Option Some None Dry::Monad Maybe Some None
  6. Maybe #bind Some: • Yields its value to the block

    • Block must return a Maybe • Returns the result of the block None: • Doesn’t call the block • Returns self
  7. Maybe #fmap Some: • Similar to bind • Block doesn’t

    return a monad • (fmap wraps block result in one) None: • Same as bind • Doesn’t call the block
  8. Maybe #value_or Safe way to extract a value or default

    Some: • Doesn’t call block • Returns value None: • Calls block
  9. Bye! Paul Sadauskas Lead Architect at TextUs • github.com/paul •

    @theamazingrando Like everyone else, we’re hiring! https://textus.com/jobs This is the talk that made me want to join TextUs — Jason Taylor Ruby on Rails Podcast Ep 389, Oct 27 2021