lundi 20 juillet 2020

Why use ServiceObjects, QueryObjects, etc. rather than Lambdas?

I work on a Rails team in the healthcare industry and I'm currently refactoring a very data-driven application into ServiceObjects, QueryObjects, etc. to keep down the complexity of the logic in my models and controllers. I'm more accustomed to using a (pragmatic) functional/relational approach when developing data-driven applications.

Overall I'm enjoying my experience using Ruby and Rails again after many years of using other technology. But, I'm interested in hearing the opinions of those more experienced than myself. Particularly those who are equally comfortable with functional and object-oriented programming or using Rails for very data-driven applications or both.

Since Ruby supports functional programming, have you found good ways of refactoring complex Rails applications using functional patterns that seem reasonably idiomatic?

Do you have any thoughts on the benefits of using ServiceObjects, QueryObjects, etc. vs just using lambdas?

One of the things I've always liked about Ruby/Rails is that they're pragmatic by design so I'm not really interested in starting a debate over OOP vs FP, just learning from the experience of others and the trade offs you've found.

Perhaps an example my help:

# app/services/entry_builder.rb
class EntryBuilder < ApplicationService
  def initialize(log)
    @log = log
  end

  def call(params)
    @log # ... do something with the log and params and return a result
  end
end

# app/models/log.rb
class Log < ApplicationRecord
  ...
  def entry_builder
    @entry_builder ||= EntryBuilder.new(self)
  end

  def create_entry(params)
     entry_builder.call(params)
  end
  ...
end

vs.

# app/functions/entry_builder.rb  ???
EntryBuilder = lambda do |log, params|
  log # ... do something with the log and params and return a result
end.curry

# app/models/log.rb
class Log < ApplicationRecord
   ...
   def entry_builder
     @entry_builder ||= EntryBuilder.call(self)
   end

   def create_entry(attributes)
      entry_builder.call(attributes)
   end
end

# else where ...
EntryBuilder.call(Log.find(1), ....)

Thank you in advance for your help.

Aucun commentaire:

Enregistrer un commentaire