// Package event is the platform's shared event vocabulary: one envelope shape // (Event) and a small set of Type constants for the event meanings that // genuinely cross module boundaries. It imports only the stdlib — never an app // module, never a transport library — so every module can depend on it. // // Discipline: a Type constant earns its place only when a real producer emits it // AND it is meaningful to another module. We do not pre-declare aspirational // types. See docs/platform-event-messaging-sketch.md. package event import ( "context" "encoding/json" "time" ) // Type is the semantic event kind — the cross-module contract. A module may use // its own private kinds internally; only meanings that cross a boundary belong // here. type Type string const ( // Editorial (graph-cms). Emitted by the CMS outbound bridge on content-graph // mutations. In-repo consumers are TBD (external generators today); this is // the first envelope producer, so the constants exist where they are emitted. ContentCreated Type = "content.created" ContentUpdated Type = "content.updated" ContentDeleted Type = "content.deleted" // Catalog (finder). These meanings already cross modules today — finder // writer → reader and → backoffice (MountFinderListener) — but still travel // as finder's legacy ChangeTopic strings on the wire. The constants document // the shared vocabulary; the wire migration onto the envelope is a later step. CatalogItemChanged Type = "catalog.item_changed" // legacy: finder "item_added" CatalogSnapshotReady Type = "catalog.snapshot_ready" // legacy: finder "snapshot_ready" CatalogSettingsChanged Type = "catalog.settings_changed" // legacy: finder "settings_change" CatalogFacetChanged Type = "catalog.facet_changed" // legacy: finder "facet_change" CatalogAccessoryChanged Type = "catalog.accessory_changed" // legacy: finder "accessory_change" // CatalogProjectionPublished carries the commerce-facing catalog Projection // (platform/catalog) — producer maps raw items → Projection once, consumers // (cart / checkout / order / UCP) read the typed shape. Distinct from // CatalogItemChanged (raw bodies, consumed by inventory/backoffice): a // product index emits BOTH; a non-product index emits neither projection. // Payload is a []catalog.ProjectionUpdate batch (upserts + deletes per SKU). CatalogProjectionPublished Type = "catalog.projection_published" CatalogSortOverride Type = "catalog.sort_override" // finder-internal: per-session/group/popularity overrides CatalogFieldSortOverride Type = "catalog.field_sort_override" // finder-internal: live field ordering overrides // Inventory (go-redis-inventory core, emitted by the cart-inventory service). // A coarse stock-level crossing (none/low/high) for one SKU+location — // never a quantity. Exact stock is a synchronous query against the inventory // service; only level crossings cross a module boundary. Payload is an // inventory.LevelChanged (platform/inventory). First consumer: finder stock // overlay (display hints). See docs/inventory-shape.md. InventoryLevelChanged Type = "inventory.level_changed" // Commerce (order). Emitted by the order service saga after an order is // placed-and-paid (a durable outbox event). The authoritative "a sale // happened" fact — multiple modules react independently (inventory commits // stock, fulfillment allocates) without checkout coupling to any of them. // Payload is an order.Created (platform/order). See docs/inventory-shape.md // and docs/fulfillment-stance.md. OrderCreated Type = "order.created" // Analytics (tracking). Replaces the integer enum 0–6 that finder and // slask-tracking use on the `tracking` topic today. Producer: slask-finder // (pkg/tracking/rabbit-tracking.go). Consumer: slask-tracking // (pkg/events/rabbit-tracking.go). TrackSession Type = "track.session" // 0 — session start TrackSearch Type = "track.search" // 1 — search query TrackClick Type = "track.click" // 2 — item click / product view TrackCart Type = "track.cart" // 3,4 — add to / remove from cart TrackImpression Type = "track.impression" // 5 — impression TrackAction Type = "track.action" // 6 — custom action (wishlist, etc.) TrackPageView Type = "track.page_view" TrackPageEngaged Type = "track.page_engaged" TrackPageAction Type = "track.page_action" TrackExperiment Type = "track.experiment" ) // Event is the one envelope. Payload is per-Type, agreed between producer and // consumer (json.RawMessage, not a god-struct). A nil Payload means "the subject // changed; fetch the latest state." type Event struct { ID string `json:"id"` // unique; enables idempotent/at-least-once dedupe Type Type `json:"type"` // the vocabulary Source string `json:"source"` // module identifier ("graph-cms", "finder", ...) Subject string `json:"subject"` // the resource that changed (node id, sku, ...) Payload json.RawMessage `json:"payload,omitempty"` // per-Type body, or nil Time time.Time `json:"time"` Meta map[string]string `json:"meta,omitempty"` // trace id, causation, country, dataType, ... } // Handler consumes an event. Returning an error means "not handled" — durable // transports may redeliver; fire-and-forget transports just log. type Handler func(ctx context.Context, e Event) error