// Package inventory is the platform's shared inventory seam: the cross-module // vocabulary for stock *levels*. It deliberately carries no quantities — exact // stock is queried synchronously from the inventory service at decision points // (PDP, add-to-cart, checkout reserve), and only coarse level crossings travel // over the bus. Putting exact quantity on the bus spams it and ships a number // that is stale the moment it is published; a level crossing is the only // inventory signal other modules receive. See docs/inventory-shape.md. // // Like platform/catalog, this package holds only the wire value types plus the // single classification function, so the producer and every consumer agree on // the cutoffs. It imports only the stdlib — never an app module, never a // transport library. package inventory import "encoding/json" // Level is the coarse, bus-safe stock state. Consumers map a Level to a display // hint / availability flag; they never see (or need) the exact quantity. type Level string const ( LevelNone Level = "none" // nothing available to sell LevelLow Level = "low" // at or below the low watermark, but still > 0 LevelHigh Level = "high" // comfortably above the low watermark ) // LevelFor classifies an exact on-hand quantity into a Level given the low // watermark. It is the single source of the cutoffs: the producer computes the // emitted level with it, and any consumer that needs to reclassify a quantity // (e.g. after a synchronous exact query) uses the same function so the two can // never disagree. qty <= 0 → none; 0 < qty <= low → low; qty > low → high. func LevelFor(qty, lowWatermark int64) Level { switch { case qty <= 0: return LevelNone case qty <= lowWatermark: return LevelLow default: return LevelHigh } } // InStock reports whether the level means the SKU can be sold (any level other // than none) — a convenience for consumers that only care about the boolean. func (l Level) InStock() bool { return l != LevelNone } // LevelChanged is the payload of an event.InventoryLevelChanged envelope: the // SKU + location whose level crossed a threshold, and the new level — never a // quantity. The envelope's Subject duplicates SKU; Meta["country"] carries the // tenant/country for consumer routing (matching the catalog wire convention). type LevelChanged struct { SKU string `json:"sku"` Location string `json:"location"` Level Level `json:"level"` } // Encode serialises the payload for event.Event.Payload. Kept here so producer // and consumers share the exact wire shape; any future renaming lands here. func (c LevelChanged) Encode() ([]byte, error) { return json.Marshal(c) } // Decode parses an InventoryLevelChanged payload — the consumer side of Encode. func Decode(raw []byte) (LevelChanged, error) { var c LevelChanged err := json.Unmarshal(raw, &c) return c, err }