dry-rb / dry-transaction

Business transaction DSL
http://dry-rb.org/gems/dry-transaction
MIT License
468 stars 55 forks source link

Plans for 1.0.0 #127

Closed timriley closed 4 years ago

timriley commented 5 years ago

Please see the comments as the discussion has gone into a different direction after all


Original description that's now obsolete

A lot has changed since dry-transaction was brought into the dry-rb family in 2016: dry-monads was later created, and for the last year, it has offered its Do Notation feature, which provides an expressive API for chaining dependent sequences of commands. Do notation is far more flexible than dry-transaction's simple "step" definitions while being just as easy to use, and it is now our recommended solution for this kind of behaviour.

As such, we'll be soon releasing dry-transaction 1.0.0, then ceasing further development.

Here's what will happen:

The purpose of this issue is to track all these steps.

If you have any questions, feel free to reach out on https://discourse.dry-rb.org!

michaelherold commented 5 years ago

A nice-to-have would be clear, concise examples for how to replicate the behavior of the different dry-transaction step adapters in Do Notation. For example, tee is very useful. I'm not sure if it's able to be 100% implemented in Do Notation, can it? A naive implementation would be something like:

class MyCommand
  include Dry::Monads::Result::Mixin
  include Dry::Monads::Do.for(:call)

  def call(params)
    values = yield validate(params)
    yield log(params)
    Success(values)
  end

  def validate(params)
    # ...
  end

  def log(values)
    Logger.new.info(values)
    Success(values)
  end
end

That seems like it would be right? It's a little clumsy though.

What do you think?

timriley commented 5 years ago

@michaelherold this is a good idea, I'll add it to the TODO list in the main issue body above.

As for replicating the tee behaviour, I don't think we need to do anything so special, it's just this:

def call(params)
  values = yield validate(params)

  # nothing special, just a plain old method call
  log(params)

  # do more steps here
end
no-reply commented 4 years ago

Is this something there's interest in following through on?

A few times now I've had folks react with confusion when I mention that I'm not developing dependent code anymore, since there's no mention(?) of development being phased out in the README or on dry-rb.org

jdickey commented 4 years ago

Move dry-transaction documentation from dry-rb.org into the dry-transaction README (or alternatively, a set of markdown files in a docs/ dir)

I'd vote for the separate-docs-directory approach; a quick-and-dirty render-everything-in-a-single-Markdown-file experiment yielded a ~480-line, seven-and-a-half-rendered-pages (on a large monitor) file that felt far clunkier than the existing docs. I've added an item to my personal TODOs to write a PR for this sometime next week, health and time permitting.

I agree enthusiastically with the overall point of this issue; do notation is a far more flexible, low-DSL way to get the job done.

graudeejs commented 4 years ago

FYI, link to Do Notation is broken in description of the issue. It should be something like: https://dry-rb.org/gems/dry-monads/1.0/do-notation/

paul commented 4 years ago

I started using dry-transaction in 2018 after having used most of the command/interactor pattern gems, and fell in love with it. I have to say I am disappointed with this news. Even though it is more "magical", it provides an explicitness in its DSL that is missing from the plan do-notation.

Borrowing the example from @michaelherold:

# Do-notation
class MyCommand
  include Dry::Monads::Result::Mixin
  include Dry::Monads::Do.for(:call)

  def call(params)
    values = yield validate(params)
    values = yield transform(values)
    log(values)
    Success(values)
  end

  def validate(params)
    MySchema.call(params).to_monad
  end

  def transform(values)
    Success(values.transform_keys(&:to_s))
  end

  def log(values)
    Logger.new.info(values)
  end
end

# Transaction
class MyTransaction
  include Dry::Transaction

  step :validate
  map  :transform
  tee  :log

  def validate(params)
    MySchema.call(params).to_monad
  end

  def transform(values)
    values.transform_keys(&:to_s)
  end

  def log(values)
    Logger.new.info(values)
  end
end

The step DSL in the transaction removes a lot of the boilerplate and general "noise" present in the #call method, and to me feels a lot cleaner. It keeps the pure "business logic" isolated inside the step implementation, while the "ceremony" of managing Results is handled cleanly and explicitly by the DSL.

In our app, we've also utilized the Dry::Events notifications from each step to automatically log the step and the Result, which has been extremely useful, and seems harder to accomplish with Do-notation. We use Rails' TaggedLogger to add a tag for each Transaction being called, making it very easy to trace:

Feb 01 18:10:09 [Messaging::Ingress::HandleMessage] [Conversations::Fetch] Transaction succeeded in 53.00ms 
Feb 01 18:10:09 [Messaging::Ingress::HandleMessage] [Messaging::Attachments::Upload] Transaction succeeded in 9.02ms 
Feb 01 18:10:09 [Messaging::Ingress::HandleMessage] [Integrations::HandleMessageEvent] Transaction succeeded in 14.32ms 
Feb 01 18:10:09 [Messaging::Ingress::HandleMessage] Transaction succeeded in 341.33ms 
---
Feb 01 18:10:09 [Messaging::Ingress::LayeredReceiptParser] Failure in :validate step. Input: {"messageId"=>"m-4hwzflo4knjxjslrdnpx4da", "eventType"=>"sms", ... } 
Feb 01 18:10:09 [Messaging::Ingress::LayeredReceiptParser] Transaction failed in 0.52ms 

Dry::Transaction also does a nice job of automatically pulling in other Dry building blocks, like Containers. For us, Dry::Transaction was the gateway drug to introduce most of the rest of the Dry ecosystem into our Rails application. Now, we have nearly 250 Transactions in our app. (I was amazed when I just looked that up. I was gonna guess 50. It does not feel cumbersome at all, the app is very easy to work on).

Perhaps I'm just misunderstanding Do-notation, but it seems to me that its missing a great deal of additional features that Dry::Transaction provides, which we've come to rely on. Is "use Do-notation instead" the only reason for discontinuing Dry::Transaction, or is it the additional maintenance burden? If the latter, I'm happy to step up and help with the maintenance. Perhaps Dry::Transaction post-1.0 could be refactored to use Do-notation internally, but still expose the step DSL and the Events features (or maybe thats even a separate library).

jdickey commented 4 years ago

@paul I can well understand that sentiment. I happen to prefer do notation for what I suspect is the exact opposite reason to your dislike of it: it utilises an instance method (#call), and thus allows/encourages the step methods it calls to be private. The dry-transaction DSL, admittedly cleaner and more explicit in several respects, at least encourages the methods called by the DSL class methods (e.g., step) to be public methods. That is frowned upon nowadays much more than it was a few years ago, when DSLs were all the rage.

It's not so much that dry-transaction has bugs that are too troublesome/expensive to fix, but that it's predicated on a coding style that a) is no longer as widely used as it had been, and thus b) does not mesh well with other current and plausible future directions of the dry-rb ecosystem.

At least that's how it appears to this outside, non-core-team user and observer. Feel free to throw popcorn and/or hand grenades as appropriate, all. 😀

solnic commented 4 years ago

Perhaps Dry::Transaction post-1.0 could be refactored to use Do-notation internally, but still expose the step DSL and the Events features (or maybe thats even a separate library).

This is an idea worth to explore. If it can be done easily I'd be in favor of keeping dry-transaction alive.

gottfrois commented 4 years ago

I want to add my 2 cents here and say that I also really like the simplicity of dry-transaction which, in my opinion, reads better than the do notation. For us as well it was the gateway to using more and more every day dry-rb gems and still is.

The do notation seems like something that should be used to build lower-level components and is missing a more user-friendly interface on top (dry-transaction anyone?).

paul commented 4 years ago

I haven't actually tried it, but isn't "refactor dry-transaction to use do-notation" basically:

include Dry::Monads::Do.for(:call)

def call(input)
  steps.reduce(Success(input)) do |result, step| 
    yield step.call(result)
  end
end
solnic commented 4 years ago

@paul I implemented a PoC a couple of weeks ago, see https://gist.github.com/solnic/66d663790a956fc811ee376249ec0447

timriley commented 4 years ago

Folks, we’re going to find a way to keep this gem around. It’s lower priority than the current hanami integration work but we’ll aim to get it done before all the dry-rb gems hit 1.0

On 29 Feb 2020, at 5:39 am, Piotr Solnica notifications@github.com wrote:

 @paul I implemented a PoC a couple of weeks ago, see https://gist.github.com/solnic/66d663790a956fc811ee376249ec0447

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub, or unsubscribe.

no-reply commented 4 years ago

@timriley thanks for this update.

i'm in the position of making a decision about moving forward with this gem, or with a localized wrapper around Do Notation. until this message, i assumed the localized wrapper was the best approach. but if there's a commitment to keeping this alive, and some guidance about what to expect/how to be involved, that would be a boon.

how can i help?

jcmfernandes commented 4 years ago

@timriley given your last statement, shouldn't the disclaimer present on the website about deprecation be removed?

solnic commented 4 years ago

@jcmfernandes thanks for pointing it out! I just changed it instead of deleting it though.

rinaldifonseca commented 4 years ago

@timriley @solnic I am starting a new project. Should I use dry-transaction without worry about the future?

timriley commented 4 years ago

I think you should be fine. The next evolution of this gem might not retain every current feature/behaviour, but we'll have to provide clearly documented migration paths for current users no matter what.

Codcore commented 4 years ago

Hi, guys! I really like dry-transaction gem, and use it massively. Is there chance to see new versions soon?

solnic commented 4 years ago

@Codcore hey, this has to wait until after hanami 2.0 is released, unless somebody steps up and takes over the maintenance.

timriley commented 4 years ago

In the meantime I'm going to close this issue. As I mentioned above, people can feel free to use 0.13.0, and for any subsequent releases, we'll be sure to provide clear migration paths for any aspects of the feature set that may significantly change.

tiev commented 3 years ago

I've used dry-transaction on production for a CSV importing process. And I've refactored it to use Do-notation.

My experience was very positive. Do-notation gave a cleaner code and easier to understand the reason for each step.

def call
  user = yield find_user
  company = yield find_company
  employment(user, company)
end

I will never go back to dry-transaction after writing code in Do-notation. I feel it's more matched with the style of whole dry-rb gems, where we can combines gems for only the features we need. dry-transaction bundle many features inside a DSL that I feel not very easy to use (mainly because of the data flow).

solnic commented 3 years ago

@tiev thanks for sharing this experience! I feel like many people like dry-transaction because it's just easier to get started and it feels more familiar, that's all.

pboling commented 2 years ago

https://dry-rb.org/gems/dry-transaction/master/ The warning is still in latest docs. It appears from this discussion, and the fact that dry-transaction is now based on dry-monads, that dry-transaction survives? Will there be a 1.0 release?

solnic commented 2 years ago

It appears from this discussion, and the fact that dry-transaction is now based on dry-monads

We're considering rebuilding it on top of dry-monads

dry-transaction survives? Will there be a 1.0 release?

Double yes 🙂

ShalokShalom commented 2 years ago

@tiev I feel like many people like dry-transaction because it's just easier to get started and it feels more familiar, that's all.

Oh yeah. I was just reading the documentation and thought exactly this.

I am coming from FSharp, and dry-rb seem to introduce already a bit more to the syntax as I am used to.

dry-transition is a welcome exception and I would welcome much more libraries like this, not less. ✌🏻

I would also consider re-opening this issue.

Thanks a lot 😊

santiagodoldan commented 7 months ago

I use dry-transaction on several projects and I'd love to understand if there are plans to get a 1.0 version in 2024, I'd love to help