adomokos / light-service

Series of Actions with an emphasis on simplicity.
MIT License
837 stars 67 forks source link

Have some kind of an `outcome` object to group all outcome-related information #163

Open ramontayag opened 5 years ago

ramontayag commented 5 years ago

We use LightService as intended: to do pretty complex processes. Many of the actions may fail, and we would like to return more information to the thing that executed the organizer (like a Rails controller), besides the message and error code.

Take Stripe's "outcome" as an example (thanks @ace-subido). To implement this with LS, we'd need to flood the contexts with more information. If there existed an outcome object that would hold the success/failure, error_code, message, and metadata (reason, risk_level would live here), the controller could expose this as they wish to the API user.

What do you think of this? Can flesh it out more if it looks interesting.

adomokos commented 5 years ago

Yep, that makes sense. I would have one caveat: let's keep it simple for folks who want to use LS just for simple scenarios.

I've started using Haskell professionally a couple of months ago, and I really enjoy how the language forces me to think about the "what if things don't work" use cases early on. I feel LS should be going in this direction as well.

Please start with initial sketches, let's make sure it's headed in the right direction before you invest too much time in it.

Thanks!

ramontayag commented 5 years ago

Ok cool. Yes I plan to not change the current signature.

I'm thinking of the following (pseudo code):

class LightService::Context
  delegate :success?, :failure?, :error_code, :message, to: :outcome
  attr_accessor :outcome

  def fail!(message, data)
    self.outcome = Outcome.new({success: false, message: message}.merge!(data))
  end
end

class LightService::Outcome
  attr_accessor :success, :message, :error_code, :metadata

  def initialize(success: true, message: nil, error_code: nil, **metadata)
    self.success = success
    self.message = message
    self.error_code = error_code
    self.metadata = metadata
  end

  def success?
    !!self.success
  end

  def failure?
    !success?
  end
end
adomokos commented 5 years ago

Looks like a good start! Go for it!

ramontayag commented 5 years ago

Just an update. We've started using this in our app first, and just monkey-patching LS to behave the way intended. Soon, we should be able to make a pull request for this.

adomokos commented 5 years ago

Yeah, that's how we added most of the functionality as well. First monkey-patch and see if it sticks. When it did, that got in as a PR. Thanks for the update!

gee-forr commented 5 years ago

Hey there, just chiming in, as I realised, it would be great if LS could tell me on which action in an organiser was the action that failed. This would be some super handy data when needing to debug. This seems like a perfect bit of data that should live in metadata. Something like:

my_org = MyOrganiser.call(foo: :bar)

my_org.success? # => false
my_org.outcome.metadata.failing_action # => MyBrokenAction

@ramontayag - is there any chance you will be releasing your changes soon, as an addon to the LS gem or a PR to the gem itself?

ramontayag commented 5 years ago

Yeah sure, pretty much anything can be thrown in there. We put it in an app but haven't extracted it yet. I will look for time to do so!