Files
platform/event/event.go
T
2026-06-28 17:52:49 +02:00

96 lines
5.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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