Event Flow
Flow Diagram
graph LR
subgraph Membership
E1["Registered"] --> L1["SendEmailVerificationNotification"]
E2["Verified"] --> L2["VerifyUserListener"]
E3["EmailChanged"] --> L1
end
subgraph Lottery
E4["LotteryApplied"] --> L4["LotteryAppliedListener\nLotteryAppliedNotification queued"]
E5["LotteryUpdated"] --> L5["LotteryUpdatedListener\nLotteryUpdatedNotification queued"]
E6["LotteryWon"] --> L6["LotteryWonListener\nLotteryWonNotification queued"]
end
subgraph Order
E7["OrderFinished"] --> L7["SendOrderFinishNotification\nlistener sync\nmail queued"]
E8["OrderShipped"] --> L8["SendShipmentNotification\nlistener sync\nmail queued"]
end
subgraph Inquiry
E9["InquirySent"] --> L9["InquirySentListener\nMail::send() fully sync"]
end
Purpose
Decouples side effects (email, user state updates) from the primary business action. All 9 events are synchronously dispatched — the async boundary is at the notification layer (most notifications implement ShouldQueue).
Event Registry
| Event |
Listener |
Notification |
Delivery |
Registered |
SendEmailVerificationNotification |
VerifyEmailNotification |
queued mail |
Verified |
VerifyUserListener |
— |
sync DB update |
EmailChanged |
SendEmailVerificationNotification |
VerifyEmailNotification |
queued mail |
LotteryApplied |
LotteryAppliedListener |
LotteryAppliedNotification |
queued mail |
LotteryUpdated |
LotteryUpdatedListener |
LotteryUpdatedNotification |
queued mail |
LotteryWon |
LotteryWonListener |
LotteryWonNotification |
queued mail |
OrderFinished |
SendOrderFinishNotification |
OrderFinishNotification |
queued mail |
OrderShipped |
SendShipmentNotification |
ShipmentNotification |
queued mail |
InquirySent |
InquirySentListener |
InquiryAutoReply + InquiryNotification |
sync Mail::send() |
Registration: app/Providers/EventServiceProvider.php
Dispatch Points
| Event |
Dispatch Location |
Registered |
RegisterController (web) |
Verified |
Email verification controller + email-change verify controller |
EmailChanged |
MyPage ChangeEmail controller |
LotteryApplied |
LotteryController::apply() |
LotteryUpdated |
LotteryController::update() |
LotteryWon |
SendLotteryWinnerEmailJob (scheduled, every minute) |
OrderFinished |
SbpsPaymentController::updateStatus() + AtoneWebhookController::notify() |
OrderShipped |
Admin ShippingController (import CSV) |
InquirySent |
InquiryController |
Lifecycle Steps
- Business action completes (order confirmed, lottery result published, etc.)
event(new EventClass(...)) dispatched synchronously
- All listeners execute synchronously within the same request
- Listeners that send notifications:
$notifiable->notify(new NotificationClass()) — most notifications implement ShouldQueue → pushed to queue driver (Redis)
- Queue worker picks up notification → sends mail via configured mail driver
Exception: InquirySentListener uses Mail::send() directly — fully synchronous, blocking the HTTP response.
Laravel Components Involved
app/Providers/EventServiceProvider.php — event → listener map
app/Modules/*/Events/ — event classes
app/Modules/*/Listeners/ — listener classes
app/Modules/*/Notifications/ — notification classes (ShouldQueue)
Data Mutations
| Event |
Mutation |
Verified |
VerifyUserListener marks user as verified in DB |
LotteryWon |
SendLotteryWinnerEmailJob sets lotteries.is_sent = true after dispatch |
Failure Paths
| Scenario |
Behaviour |
| Queue worker down |
Queued notifications accumulate in queue; no immediate failure |
InquirySentListener mail failure |
Sync failure — throws exception, 500 response to submitter |
LotteryWonNotification::setCartUrl() on empty collection |
Throws — winner email fails if cart URL cannot be built |
SendOrderFinishNotification duplicate getOrderProducts() call |
Two DB queries on every order confirmation email |
Security Considerations
LotteryWon is dispatched inside SendLotteryWinnerEmailJob (scheduled) — is_sent flag set after event() but before queue worker delivers the notification. If worker fails, is_sent is still true → no retry → winner never notified.
- No event sourcing or audit log of dispatched events.
- All listeners are synchronous — heavy listeners block the HTTP response
InquirySentListener blocks inquiry form submission on every mail send
- Notification queue depends on Redis availability; if Redis is down, notifications are dropped or lost depending on queue driver config
SendOrderFinishNotification calls getOrderProducts() twice (duplicate query)