hirethunk / verbs

Verbs is an event sourcing package for PHP artisans
https://verbs.thunk.dev
MIT License
412 stars 32 forks source link

Add support for outside listeners #145

Open inxilpro opened 3 months ago

inxilpro commented 3 months ago

This lets you register outside listeners for events. Say you have an analytics process, or some secondary reporting functionality that you want to add. Sometimes it doesn't make sense for those to exist on the event itself. Now you can do something like:

class AnalyticsProjector
{
  // This will implicitly run when `handle` is called
  public function onDownload(ProductDownloaded $event)
  {
    Analytics::incrementDownloads();
  }

  // Or you can use attributes to be explicit and set phases
  #[Listen(ProductDownloaded::class)]
  #[Listen(ProductUpdated::class)]
  #[Listen(ProductDiscontinued::class)]
  #[On(Phase::Fired)]
  public function genericListener($event)
  {
    // will get called during the "fired" phase when any of those events fire
  }
}

Then you just register with:

Verbs::listen(AnalyticsProjector::class);
inxilpro commented 3 months ago

Ugh. I accidentally ran pint on everything. I'll revert before marking as ready.

inmanturbo commented 2 months ago

What is the status on this? Is it currently possible to listen or subscribe to verb events outside of the event definition itself?

In our case the event itself will be inaccessible to the developer writing projections in some cases, as it will be in another code base (composer package) handled by another team. In other words for some parts of the system we will have one development team publishing events and another team subscribing to them.

inmanturbo commented 2 months ago

As a workaround I'll be testing a POC using circular dependency. I.E.:

// after apply()

public function handle()
{
    $listeners = app(CustomerHandlers::class)->getListeners();

    foreach ($listeners as $listener) {
        $listener->handle($this);
    }
}
// meanwhile, somewhere in a whole 'nother code base ...

class SubscriptionHandler implements CustomerHandler
{
    public function handle(Event $event): mixed
    {
        return match(get_class($event))  {
            CustomerBeganTrial::class => Subscription::create([
                'customer_id' => $event->customer_id,
                'expires_at' => now()->addDays(30),
            ]),
            default => null,
        };
    }
}