Events

Events let one part of your app notify others without knowing who is listening. Fire an event once; every registered listener runs — synchronously, in order of registration, in the same thread.

Event API

An event is a small value class derived from Garvan::Event:

// app/events/UserRegistered.h
#include "events/Event.h"
#include "tools/JsonValue.h"

namespace AppEvents {

class UserRegistered : public Garvan::Event
{
public:
    UserRegistered() = default;
    UserRegistered(int id, std::string mail, std::string person)
        : user_id(id), email(std::move(mail)), name(std::move(person)) {}

    std::string eventName() const override { return "UserRegistered"; }

    Garvan::JsonValue payload() const override;
    static std::unique_ptr<Garvan::Event>
        fromPayload(const Garvan::JsonValue& p);

    int         user_id{};
    std::string email;
    std::string name;
};

} // namespace AppEvents

Writing a listener

Derive from Garvan::Listener<YourEvent> and implement handle():

// app/listeners/SendWelcomeEmailListener.h
#include "events/Listener.h"
#include "../events/UserRegistered.h"

class SendWelcomeEmailListener
    : public Garvan::Listener<AppEvents::UserRegistered>
{
public:
    void handle(const AppEvents::UserRegistered& event) override;
};

The listener body can do anything — write to the database, call a service, or dispatch a job (which is exactly what SendWelcomeEmailListener does, forwarding to AppJobs::SendTestMail).

Firing events

#include "events/EventDispatcher.h"
#include "app/events/UserRegistered.h"

Garvan::EventDispatcher::fire(
    std::make_unique<AppEvents::UserRegistered>(42, "u@x.com", "Ada"));

Listeners are called synchronously in the same thread; if one throws, subsequent listeners are still executed and fire() re-raises the first exception at the end.

Registering in a service provider

Two registrations happen in EventServiceProvider::register_():

// app/providers/EventServiceProvider.cpp
Garvan::EventDispatcher::listen<AppEvents::UserRegistered,
                                LogRegistrationListener>();
Garvan::EventDispatcher::listen<AppEvents::UserRegistered,
                                SendWelcomeEmailListener>();

// Optional: register the event's factory for admin-API `event:fire`
GARVAN_REGISTER_EVENT(AppEvents::UserRegistered);

Unlike GARVAN_REGISTER_JOB, the event macro is fine: the event's dispatch key is taken from eventName() at runtime, not from the stringified type name.

Providers are booted in main.cpp:56-60 in a fixed order — AppServiceProvider first (queue drivers), then JobServiceProvider, then EventServiceProvider. Listeners can safely dispatch jobs at boot time because jobs are already registered.

Example: UserRegistered chain

GET /api/events/user-registered?email=...&name=...&id=...
  → EventDispatcher::fire(UserRegistered)
      → LogRegistrationListener::handle()      (sync log line)
      → SendWelcomeEmailListener::handle()
           → JobDispatcher::dispatch(SendTestMail)
                → SyncDriver (inline)
                     → SendTestMail::handle()  (libcurl SMTP send)

See Events & Jobs walkthrough for an end-to-end curl-and-verify tour.