Shamrock Stakes is an internal event app: employees bet play money on silly, day-long outcomes (“Will someone mention Kubernetes before lunch?”). No real money changes hands—the goal is engagement, ice-breaking, and a harmless competitive vibe.
Technically it is a small Laravel + realtime product with the same discipline as customer software: auth, auditability, and deployment that survives one noisy Wi‑Fi.
1. Product primitives
| Model | Responsibility |
|---|---|
Party |
Event container: time window, branding, invite list |
Market |
One question; binary or small multi-outcome |
Outcome |
Possible resolutions for a market |
Position |
User stake on an outcome (integer chips) |
LedgerEntry |
Append-only accounting for bankroll changes |
Settlement
- Admin closes market → job locks rows (
SELECT … FOR UPDATEor DB transaction) → computes payouts from parimutuel or fixed-odds rules (we used simple house rules, documented in UI). - Writes ledger lines + broadcasts UI refresh.
2. Realtime: Laravel Echo + Reverb
Channels
party.{id}— roster + “markets added” events (PartyMarketsUpdated)market.{uuid}— odds / pool updates (MarketOddsUpdated)
Storefront blade pattern
- Inline script waits for
window.Echo(loaded from Vite bundle) with bounded retries—conference Wi‑Fi often delays JS. - Subscribe per visible market card; unsubscribe on
turbo:before-visit/ page unload if using Turbo-like navigation.
Server
- Events implement
ShouldBroadcast; usebroadcast(new MarketOddsUpdated(...))->toOthers()when appropriate to avoid echo loops.
Auth
PrivateChannel/PresenceChannelif you need “who is online”—we kept public party channels + server-side authorization on mutating routes.
3. Laravel Cloud / deployment
- TLS everywhere—mixed content blocks WS.
- Environment:
REVERB_APP_ID, keys,VITE_REVERB_*must match between Pusher-compatible client config and server. - Horizontal scale: sticky sessions not required for API if JWT/Sanctum; websockets need Reverb scaled with Redis adapter—document concurrent connection limits for the venue.
4. UX safeguards
- Max stake per market and per user to prevent one person “ending the joke.”
- Explain this is not gambling in copy + email footers.
- Admin undo window before settlement finalizes—mistakes happen on stage.
5. Testing
- Feature tests on settlement math (property-based tests for chip conservation: sum before = sum after).
- Load test a fake burst of broadcasts locally with
artisan websockets:serve+k6hitting HTTP endpoints (not WS deep load—good enough smoke).
6. What we learned
- Projector screens need large fonts and high-contrast odds—design for the back row, not the laptop.
- Echo fallback: if websocket fails, poll
/api/parties/{id}/snapshotevery N seconds—ugly but saves the event.
Resume framing
“Shipped Laravel + Reverb/Echo realtime for an internal prediction-market experience: broadcast design, settlement transactions, resilient client subscribe, and cloud-friendly deploy.”