This commit is contained in:
mats committed 2026-06-28 17:52:49 +02:00
1 parent e706f7d907
commit 21bcb63440
42 files changed
+5903

No files matched your search

+95
View File
@@ -0,0 +1,95 @@
// 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"
)
// 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