Hey folks. I wanted to mention that I published the first version of the Transactable gem today which might be of interest to people here. Basically, it’s a gem which builds upon native function composition support in Ruby. In some sense, you could say it’s a super-charged version of Dry Monads. Anyway, thought I’d post this here in case it’s of interest to others. Enjoy!
Transactable looks powerful and comprehensive, @bkuhlmann! Thank you so much for sharing it, and for building something that can use and fit so nicely with the rest of our gem ecosystem
I still have it on my to-do list to revitalise/reinvent dry-transaction (hopefully at the start of next year). If you’d be up for it, I’d love to have a chat with you sometime about the thinking behind Transactable and what kind of things are important to you for gem like this.
Hey Tim, thanks! It’s early days for the gem but I’m happy with the results and is making the work I do easier so hopefully it’s of benefit to others as well.
Yeah, I’d definitely be interested in talking about this more as well since I use Dry RB heavily and definitely would like to see more advancements made within this space. I have a LOT that I could talk about since I’ve got so many notes/ideas on potential improvements to Dry Containers, Dry Monads, Dry Effects, and others.
Regarding Dry Transaction, I can provide a bit more context if it helps. To be frank – and apologies if this is a bit of a downer – Dry Transaction is my least favorite gem within the Dry RB ecosystem. When I was first introduced to it, I was working in a toxic environment that was way too heavy handed with it’s use of Dry Transaction. Since that experience, it’s taken me some time to recover and articulate – the result of which is the Transactable gem – as to what made Dry Transaction a rough design. Don’t get me wrong, there are some good ideas in Dry Transaction (I mean, I did use it as a starting point for my own design)! …but the primary approach has major architectural flaws:
- Most methods are public by default which breaks object encapsulation (doesn’t help that Auto Inject encourages this design where dependencies are public by default as well).
- You end up having to over abuse the use of double splats (keyword splats) or message forwarding, in general, between the steps. This makes debugging and message passing much harder to read, understand, and debug.
This is where the Transactable gem comes into play because it provides an antidote to the the above by:
- Encouraging good design, by default, so your Object API is tightly limited in scope (i.e. generally only need
#callas your single public API to your object).
- All behavior is private by default which means you have easier opportunity to refactor without breaking downstream dependencies.
- A much simpler design since all steps are composable (i.e. function composition) which is essentially
- Testing is greatly simplified as well because it’s much easier to inject a spy for testing purposes (or stub container dependency) without the need to be digging into the steps or swapping the steps out entirely like you would with Dry Transaction.
Anyway, I hope this is of help and provides a bit more context to your question. I’m also happy to chat more about this with you as well. Just let me know when you’d have some time. I’m currently on Mountain Daylight Time (UTC -6) while you are UTC +10 (I think) which makes things a little dicey but happy to sync up if you have time.