Event Subject Naming Convention
skyflow beginner 3 min read
ELI5
Every Skyflow event subject is a dotted name like events.user.registered. The first segment is always events., the second is the domain (user, subscription, timeline…), and the third is a past-tense verb describing what already happened. Going more than four segments deep is forbidden so wildcard subscriptions stay readable.
Technical Deep Dive
Pattern
events.<domain>.<action>[.<sub>]Rules from nats/topology.md § Subject Naming Convention:
- Always start with
events. <domain>= service or entity (user,subscription,timeline,xp,achievement,streak,realtime)<action>= past-tense verb (registered,updated,completed,earned,unlocked)- Maximum 4 levels deep
Canonical Subjects
| Subject | Producer | Stream |
|---|---|---|
events.user.registered | core-api | USER |
events.user.updated | core-api | USER |
events.user.deleted | core-api | USER |
events.subscription.created | core-api | SUBSCRIPTION |
events.subscription.payment_failed | core-api | SUBSCRIPTION |
events.timeline.requested | core-api | TIMELINE |
events.timeline.completed | timeline-service | TIMELINE |
events.timeline.failed | timeline-service | TIMELINE |
events.xp.earned | gamification-service | GAMIFICATION |
events.achievement.unlocked | gamification-service | GAMIFICATION |
events.streak.broken | gamification-service | GAMIFICATION |
events.realtime.xp_earned | gamification-service | REALTIME |
events.realtime.tier_limit_warning | core-api | REALTIME |
dlq.<original_subject> | event-router | DLQ |
Wildcard Subscription Examples
flowchart TD F1["events.>"] -->|all events| ER[event-router] F2["events.user.>"] -->|all user events| GU[user-indexer] F3["events.timeline.requested"] -->|exact| TP[timeline-processor] F4["events.xp.*"] -->|earned, deducted| LU[leaderboard-updater] F5["events.realtime.>"] -->|all realtime| RT[realtime-gateway]> matches one-or-more trailing tokens; * matches exactly one token.
Key Terms
>wildcard → multi-token tail match (events.user.>coversevents.user.registered.email_senttoo)*wildcard → single-token match- Past-tense action → events describe facts that already happened, never imperatives or requests; the only “request-shaped” subject is
events.timeline.requestedwhich is itself past tense (the request was made) - Subject depth = 4 max → keeps wildcard reasoning tractable
Q&A
Q: Why must the action be past tense?
A: Events represent immutable history. A subject like events.user.update would imply a command; events.user.updated implies it has already happened, which is what NATS publishes.
Q: Where does the DLQ subject come from?
A: Event Router computes dlq.<original_subject> (e.g. dlq.events.timeline.completed) and publishes the failed payload there. The DLQ stream binds dlq.>.
Q: Can a subject have five segments? A: No — the convention caps at four. Stuff additional context into the protobuf payload, not the subject.
Q: Why is events.realtime.xp_earned separate from events.xp.earned?
A: They are different subjects on different streams with different durability. events.xp.earned is the durable XP ledger event; events.realtime.xp_earned is the ephemeral push-notification trigger derived from it.
Examples
Subjects are like postal addresses. events.user.registered is “Country.City.Street”. You wouldn’t write a five-line address — > is “anywhere on this street”, * is “this exact house number on any street with this prefix”.
neighbors on the map
- NATS Subject Taxonomy wiring a new consumer to the right stream
- CI Transition Event Schema vendoring kahn_emit.py into a CI producer