Mr. Hook
Mr. Hook

Reputation: 63

Hooks or Events to reduce coupling between packages

What is the best approach to reduce coupling between modules?

For example, if I have an Invoice package and it is related to theCustomer package.

What I understand is that I would have to use a system of "hooks" to inject, for example, a tab with the list of customer invoices in the edit view of a customer.

And in turn, use an event system to know, from the perspective of the "Invoice" package, when, for example, someone tries to delete a client.

What I want to achieve is to reduce the coupling so that if I delete the invoice package, the client package is not affected.

How can I get it? Using Laravel's event system? Using a custom class like the following?

My Hooks class:

class HookRepository
{
/**
 * The repository items.
 *
 * @var \Illuminate\Support\Collection
 */
protected $items;

/**
 * Create a new repository instance.
 *
 * @return void
 */
public function __construct()
{
    $this->items = collect();
}

/**
 * Dynamically call methods.
 *
 * @param  string  $method
 * @param  array  $arguments
 * @return mixed
 */
public function __call(string $method, array $arguments)
{
    return $this->items->{$method}(...$arguments);
}

/**
 * Register a new hook callback.
 *
 * @param  string|array  $hook
 * @param  callable  $callback
 * @param  int  $priority
 * @return void
 */
public function register($hook, callable $callback, int $priority = 10): void
{
    $this->items->push(compact('hook', 'callback', 'priority'));
}

/**
 * Apply the callbacks on the given hook and value.
 *
 * @param  string  $hook
 * @param  array  $arguments
 * @return mixed
 */
public function apply(string $hook, ...$arguments)
{
    return $this->items->filter(function ($filter) use ($hook) {
        return !! array_filter((array) $filter['hook'], function ($item) use ($hook) {
            return Str::is($item, $hook);
        });
    })->sortBy('priority')->reduce(function ($value, $filter) use ($arguments) {
        return call_user_func_array($filter['callback'], [$value] + $arguments);
    }, $arguments[0] ?? null);
}
}

Upvotes: 1

Views: 300

Answers (1)

sebastian_t
sebastian_t

Reputation: 2869

Using interfaces and abstract classes to implement Open/Close principle from SOLID

  1. Identify an interface. What ClassA wants from ClassB
  2. Generalize - when you have an interface you will be able to identify common operations that most classes that need to implement it will need it. ( Don't try to future-proof to much here or it will most likely backfire )

Note: If you are doing this in hope you will avoid refactoring your code. Forget it. :) It will only make it a lot easier which already is a huge benefit.

Avoid using hooks to realize Structural and/or Behavioral patterns.

edit> Neither Package nor Hook is a part of Laravel or Design Patterns lingo.

This itself should give you a hint.

Let me play a guessing game:

    <?php
    PackageInterface {
        public function enable();
        public function disable();
        public function isEnabled();
        public function getHooks(): HookInterface[];
    }
    HookInterface {
        public function injectView();
        // or maybe
        public function injectIntoView($view);
    }

It purely depends on your circumstances when you will load your packages and inject something into view.

You can for example enable() the Package when $invoice->wasPaid() or when Customer enabled the package himself $customer->enable($package)

Upvotes: 1

Related Questions