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.