CRUMB a card from devarno-cloud

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:

  1. Always start with events.
  2. <domain> = service or entity (user, subscription, timeline, xp, achievement, streak, realtime)
  3. <action> = past-tense verb (registered, updated, completed, earned, unlocked)
  4. Maximum 4 levels deep

Canonical Subjects

SubjectProducerStream
events.user.registeredcore-apiUSER
events.user.updatedcore-apiUSER
events.user.deletedcore-apiUSER
events.subscription.createdcore-apiSUBSCRIPTION
events.subscription.payment_failedcore-apiSUBSCRIPTION
events.timeline.requestedcore-apiTIMELINE
events.timeline.completedtimeline-serviceTIMELINE
events.timeline.failedtimeline-serviceTIMELINE
events.xp.earnedgamification-serviceGAMIFICATION
events.achievement.unlockedgamification-serviceGAMIFICATION
events.streak.brokengamification-serviceGAMIFICATION
events.realtime.xp_earnedgamification-serviceREALTIME
events.realtime.tier_limit_warningcore-apiREALTIME
dlq.<original_subject>event-routerDLQ

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.> covers events.user.registered.email_sent too)
  • * 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.requested which 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