Product Design — standalone architecture
If you were asked to build just this flow: business analysts & actuaries author products (line → form → provisions → options) and the rules, versioned by effective date. It is write-rarely, read-constantly (every quote reads a product version). Every other flow is a black box here — they only ask "give me the product version effective on date D" and receive an immutable document (how product versioning works — worked example). Advance the maturity stage to see it grow.
Stage 1 · MVP. A Cloud Run authoring API over one Cloud SQL database (with the bitemporal product_version tables). Authors can create and version products. Nothing else needed yet.
Stage 2 · Real product. Publish each version as an immutable artifact and emit ProductVersionPublished on the event spine, so Underwriting and Pricing can consume it. Now it's a real upstream service — other flows are black boxes that just read versions.
Stage 3 · Trust & reads. Every quote reads a version, so push the immutable published doc to a CDN and add a read replica — the hot read never touches the primary. Authoring stays low-volume, so the write side never needs more.
Stage 4 · Scale. Only if reads of product versions fan out very heavily (thousands of consumers per publish): move to AlloyDB (Profile B). Same Postgres interface → non-breaking.
📖 Flow story — what really happens
Every cross-flow arrow is the same async handoff — no flow calls another directly. 1. The sending flow commits its data row plus an outbox row in one transaction. 2. A relay publishes the event to the event bus (Pub/Sub on GCP · EventBridge→SQS on AWS). 3. The bus's push-subscription POSTs it to the receiving flow's Cloud Run API, which processes it idempotently (duplicates ignored, deduped on event_id). The getProductVersion read below is a plain synchronous request/response, NOT an event.
- A business analyst/actuary authors a product as data in the authoring API (Cloud Run): line → form → provisions → options + compliance/risk rules.
- Publishing opens one transaction and inserts a new immutable
product_versionrow (effective-dated; never an in-place edit) plus anoutboxrow holdingProductVersionPublished, then commits. - The relay publishes
ProductVersionPublished{version id, line/form, effective date} to the bus; Underwriting and Pricing consume it to warm their caches. - Reads are separate and synchronous:
getProductVersion(line, form, asOfDate)returns the immutable doc, served from a CDN + read replica so the hot read never touches the authoring primary. (Worked example: how a version is resolved.) - At bind, Pricing stamps the chosen
product_versionid onto thepolicy, so you can reproduce exactly what was shown and sold. - A new filing = a new
product_versionrow; in-force policies keep their stamped version until renewal rolls them onto the current one.
Interfaces (black-box contract)
| Direction | Contract | Who |
|---|---|---|
| Emits | ProductVersionPublished {version id, line/form, effective date} | Underwriting, Pricing (warm caches) |
| Read API | getProductVersion(line, form, asOfDate) → immutable doc | Customer, Underwriting, Pricing |
| Consumes | — (this flow is upstream of everything) | — |
No other flow touches this database. They see only the published versions + the read API — so Product can be built, deployed, and scaled on its own.