adomokos / light-service

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

Before/After Actions Hooks #144

Closed adomokos closed 6 years ago

adomokos commented 6 years ago

As we used LS for more and more complex workflows (yeah, orchestrator functionality was born), we had to add instrumentation or custom logic before/after some or all actions.

I really liked AOP concepts back in the early 2000s, the idea of injecting custom code (almost like a plug-in interface) fascinated me.

This PR introduces the before_actions and after_actions hooks in LightService Organizers. These custom hooks fire (if provided) before and after each action. They are great for instrumenting your code, yet you don't have to clutter the business logic with instrumentation.

Consider this code:

class SomeOrganizer
  extend LightService::Organizer

  def call(ctx)
    with(ctx).reduce(actions)
  end

  def actions
    OneAction,
    TwoAction,
    ThreeAction
  end
end

class TwoAction
  extend LightService::Action
  expects :user, :logger

  executed do |ctx|
    # Logging information
    if ctx.user.role == 'admin'
       ctx.logger.info('admin is doing something')
    end

    ctx.user.do_something
  end
end

Note how the logging information is blurring the actual business logic.

You could now do this:

SomeOrganizer.before_actions = 
  lambda do |ctx|
    if ctx.current_action == TwoAction
      return unless ctx.user.role == 'admin'
      ctx.logger.info('admin is doing something')
    end
  end

class TwoAction
  extend LightService::Action
  expects :user

  executed do |ctx|
    ctx.user.do_something
  end
end

Look, how the instrumentation logic has been decoupled from business logic.

The same is true for after_actions.

README update will be attached to this PR.