# Freya Domain - Architecture

> Luxury Goods and Fashion Intelligence operating system (`libs/freya/*`,
> `apps/freya/*`, `libs/contracts/freya`; TODO Phase 57). An implemented
> in-process TypeScript domain: deterministic business engines today, with a
> persistence and transport surface defined as code but not yet wired to live
> infrastructure.

Freya is the bounded context for luxury-goods businesses operating in African
fashion, beauty, textiles, jewelry, and accessories. It models the operating
system a brand or conglomerate needs to _create_ products, _source_ materials,
_manufacture_ at scale, _sell_ through multiple channels, and _analyze_
financial performance — from a designer's first concept board through to a
customer's purchase and the brand's per-business-unit P&L. It is intentionally
supply-side: Freya runs the business of making and selling luxury goods. It does
not make per-consumer styling decisions, and it does not own physical real
estate; those concerns belong to Aglaea and Cybele respectively.

Freya sits in the application/domain tier of the Oshun monorepo, not the engine
tier. Unlike Neith (a sovereign Rust runtime kernel that everything builds
_on_), Freya is a TypeScript workspace that builds _on_ shared platform
contracts and publishes domain state outward. Its principal downstream consumer
is Aglaea, the consumer-styling domain, which reads Freya's products, sizing,
inventory, campaigns, and provenance to drive recommendation experiences. The
dependency arrow points one way: no Freya library imports from Aglaea.

## What "Implemented" Means Here

The single most important thing to understand before reading further: **every
Freya capability library and application is a deterministic engine that operates
over in-process `Map`-backed stores.** The domain logic — state machines,
costing, AQL inspection, relevance scoring, formulation validation — is real,
specific, and fully implemented. The _infrastructure_ around it (PostgreSQL,
Redis, MinIO, Kafka, gRPC) is defined as typed schema and configuration code but
is **not actually connected**. Concretely:

- `apps/freya/api/src/order-routes.ts` keeps orders in
  `const orders = new Map<string, OrderResponse>()` and exposes
  `resetOrderStore()` for test isolation — there is no database call in the
  request path.
- **No capability library or application imports `@freya/db`.** A repo-wide grep
  shows `@freya/db` is referenced only by its own tests. The 24-table Drizzle
  schema is a parallel, currently-unconsumed persistence definition, not the
  backing store for the running code.
- `apps/freya/api/src/event-bus.ts` defines the CloudEvents envelope, the event
  taxonomy, the Kafka topic-routing map, and `buildFreyaEventBusConfig()`, but
  instantiates no Kafka producer.
- `apps/freya/api/src/grpc-services.ts` says so in its own header comment:
  "Defines Protobuf-style service contracts as TypeScript interfaces. Actual
  .proto files are generated from these definitions." It ships
  `generateProtoStub()`, not a running gRPC server.

This is an honest, fail-loud architecture, not a faked one: the engines compute
real results from real inputs; the persistence/transport layer is a complete
contract waiting for a wiring phase. The sections below label each layer's
status explicitly.

## Workspace Shape

Freya occupies three areas of the monorepo: twenty-six libraries under
`libs/freya/*` (two foundation, twenty-four capability), a shared contracts
package at `libs/contracts/freya`, and five applications under `apps/freya/*`.

```text
libs/freya/
  core/   db/                      # foundation (everyone depends on core)
  fashion/   textiles/   beauty/   # multi-module capability libraries
  brand/     ecommerce/  manufacturing/
  retail/    academy/    jewelry/
  market-intel/  financials/  connectors/  sota/
  watches/   perfumes/   bridal-events/    # single-module facade libraries
  hair-care/ home-decor/ footwear/
  eyewear/   luggage-leather/  personal-care/
  cleaning-products/  textile-finishing/

libs/contracts/freya/              # @freya/contracts — shared Zod schemas
apps/freya/
  api/  studio/  shop/  retail/  manufacturing/
```

## Architectural Layers

### Foundation: `@freya/core` (implemented)

`@freya/core` (`libs/freya/core/src/index.ts`) is the type-and-algorithm
foundation every other Freya package imports — dozens of source files across the
workspace depend on it. It is in-process TypeScript types and pure functions,
not database rows. Its modules are `product-types.ts`, `material-types.ts`,
`supply-chain-types.ts`, `brand-customer-types.ts`, `beauty-types.ts`,
`business-units.ts`, and `utilities.ts`.

The foundation is where Freya's domain correctness actually lives, and the
algorithms are specific rather than generic CRUD:

- **Branded identity.** Six template-literal ID types (`FreyaProductId` =
  `FPRD-…`, `FreyaOrderId` = `FORD-…`, etc.) make the compiler reject a
  `FreyaOrderId` where a `FreyaProductId` is expected. Generators build IDs as
  `PREFIX{hexTimestamp}-{6-hex-sequence}-{4-hex-random}` with a monotonic
  sequence wrapping at `0x1000000`.
- **Quality / supply chain** (`supply-chain-types.ts`): `computeAQLSampleSize`
  returns standard sample sizes per lot band; `evaluateQualityResult` returns
  `fail` on any critical defect, `conditional_pass` when minors exceed twice the
  AQL threshold, `pass` otherwise; `computeSupplierScore` is the weighted
  aggregate `performance×0.4 + onTime×0.3 + qualityPassRate×0.3`.
- **Pricing** (`product-types.ts`): `getPriceForMarket` resolves a tier, prefers
  the requested currency, and falls back via a fixed market→currency map.
- **Beauty chemistry** (`beauty-types.ts`): `validateINCISum` enforces
  ingredient percentages summing to 100% ±0.5; `computeFragranceAccordStrength`
  weights base notes ×1.5, heart ×1.2, top ×1.0.
- **Utilities** (`utilities.ts`): real EAN-13/UPC-A check-digit algorithms,
  fixed `EXCHANGE_RATES_TO_GHS`, textile unit conversions, a Ghana Kente/Ankara
  Pantone palette with nearest-color matching, and the `FreyaError` hierarchy
  (`FreyaValidationError`, `FreyaNotFoundError`, `FreyaBusinessRuleError`) used
  for fail-loud guards across every library.

### The Business-Unit Catalog and Its Facade Libraries

`business-units.ts` exports `FREYA_BUSINESS_UNIT_CATALOG`, a frozen array of
nineteen `FreyaBusinessUnitProfile` records (`code`, `packageName`,
`displayName`, `summary`, `primaryCategories`, `operatingCapabilities`,
`productionModels`, `commercialChannels`, `keyMetrics`).
`assertCompleteFreyaBusinessUnitCatalog` enforces exactly nineteen entries with
unique codes and packages.

**Honest correction to `features.md`:** eleven of the capability libraries —
`watches`, `perfumes`, `bridal-events`, `hair-care`, `home-decor`, `footwear`,
`eyewear`, `luggage-leather`, `personal-care`, `cleaning-products`, and
`textile-finishing` — are **not** domain-rich engines. Each is an 18-to-20-line
facade that reads its profile from the catalog.
`libs/freya/watches/src/index.ts` in its entirety exposes
`getWatchesBusinessUnitProfile()`, `hasWatchesOperatingCapability(capability)`,
and `listWatchesKeyMetrics()` over `getFreyaBusinessUnitProfile('watches')`.
These are honest, useful registry accessors — they fabricate nothing — but the
_operational_ logic for those units lives in the multi-module libraries:
fragrance and haircare in `@freya/beauty` (`fragrance-development.ts`,
`haircare-development.ts`); personal-care and detergent in `@freya/beauty`
(`personal-care-detergent.ts`); finishing in `@freya/textiles`
(`finishing-operations.ts`); and assembled goods (footwear, eyewear, leather) in
`@freya/manufacturing` (`assembly-manufacturing.ts`).

### Capability Libraries (multi-module, implemented)

Thirteen libraries carry the bulk of the domain logic, each a set of focused
modules over in-memory state. Every one depends only on `@freya/core`. A few
representative, code-grounded examples:

- **`@freya/fashion`** (`design-pipeline.ts`, `collection-planner.ts`,
  `size-grading.ts`, `production-scheduler.ts`, `trend-forecasting.ts`,
  `african-print-library.ts`). `DesignPipeline` is a guarded finite state
  machine over
  `concept → mood_board → sketch → pattern → prototype → sample → production`
  (with `discontinued` reachable from any stage and an `ALLOWED_TRANSITIONS`
  table that throws on illegal moves). `DesignCostEstimator` computes garment
  cost from Standard Minute Value: `laborCost = (SMV/60) × rate`,
  `overhead = labor × 0.30`. `PrototypingTracker` caps revision cycles at five.
- **`@freya/ecommerce`** (`catalog-inventory.ts`, `diaspora-commerce.ts`,
  `social-personalization.ts`). `InventoryService.computeStockLevel` returns
  `max(0, warehouseStock − activeReservations)`, treating non-committed,
  non-expired reservations as held; `reserveStock`/`commitReservation`/
  `releaseReservation` implement a TTL soft-lock with multi-warehouse draw-down
  (Accra first). `OrderManagementService.transitionStatus` enforces a
  fourteen-state DTC machine. `SearchEngine.computeRelevanceScore` is the
  documented blend `text×0.4 + popularity×0.3 + recency×0.2 + margin×0.1`.
- **`@freya/financials`** (`unit-economics.ts`, `financial-planning.ts`,
  `startup-investment.ts`). `GarmentUnitEconomics` carries named constants
  (`TRIMS_PCT_OF_FABRIC = 0.08`, `OVERHEAD_PCT_OF_CMT = 0.30`,
  `WHOLESALE_MULTIPLIER = 2.2`, `RETAIL_MULTIPLIER = 2.4`) and computes a COGS
  breakdown plus wholesale/retail ladders.
- Plus **`@freya/textiles`**, **`@freya/beauty`**, **`@freya/manufacturing`**,
  **`@freya/retail`**, **`@freya/brand`**, **`@freya/jewelry`**,
  **`@freya/academy`**, and **`@freya/market-intel`**, each following the same
  pattern: domain-specific classes and pure functions over `Map` stores, guarded
  by `FreyaBusinessRuleError`/`FreyaValidationError`.

### `@freya/connectors` (implemented as brief-builders; not wired)

`@freya/connectors` (`index.ts` re-exporting `asase-`, `brigid-`, `cybele-`,
`saraswati-`, `maat-connectors.ts`) is the _intended_ single point of
cross-domain integration. As built, it computes hand-off artifacts in-process
rather than calling the other domains. For example
`cybele-connectors.ts::FactoryConstruction.buildFactorySpec(productionType, capacity, workforce)`
derives floor area (`workforce×8 + capacity×0.5 m²`), power
(`workforce×2 + capacity×0.1 kVA`), clean-room class (ISO 8 for cosmetics), and
compliance standards (adding ISO 22716 GMP for cosmetics, OEKO-TEX/GOTS for
textiles); `FlagshipStoreDesign.computeStoreFloorArea` sizes a flagship from
peak traffic and dwell time. These are real luxury-retail/industrial heuristics
that produce a request payload — but there is **no import of, or network call
to, Cybele/Brigid/Asase/Saraswati/Maat**. The connector is the correct seam;
live dispatch across it is roadmap.

### `@freya/sota` (implemented enrichment seam)

`@freya/sota` (`ai-design.ts`, `ai-analytics.ts`, `computer-vision.ts`,
`nlp.ts`, `blockchain-auth.ts`, `virtual-tryon.ts`) houses the opt-in
state-of-the-art enrichment layer. It is deliberately separated from the
deterministic capability libraries so business logic stays testable and
predictable even when an AI enrichment is unavailable or returns an unexpected
result.

### `@freya/db` (defined-as-code; unconsumed)

`@freya/db` (`schema.ts`, `connection.ts`, `redis-config.ts`, `s3-config.ts`,
`observability.ts`, `seed.ts`) is a complete persistence _specification_:
twenty-four `freya_*` Drizzle tables (`grep -c pgTable` = 24) with UUID PKs,
`jsonb` columns, and four `vector(1536)` pgvector embedding columns;
`buildFreyaDbPoolConfig` sizing pools per service role; `FreyaRedisKeys`/
`FreyaRedisTTL`/`FreyaPubSubChannels`; three MinIO/S3 bucket policies; and an
OpenTelemetry/Prometheus/circuit-breaker config. It is real, careful schema and
config — but as noted above, nothing in the running domain imports it. The DB
integration test (`integration.spec.ts`) gates its live-Postgres assertions
behind `skipIf(process.env.FREYA_DATABASE_URL === undefined)` and otherwise
exercises pure logic (S3 key construction, INCI validation, Redis channel
naming), so the suite passes without infrastructure.

### `@freya/contracts` (implemented)

`libs/contracts/freya/src/index.ts` re-exports four Zod schema modules
(`product-schemas`, `order-schemas`, `customer-schemas`,
`manufacturing-schemas`) that form the stable, validated API boundary. These are
consumed by the API app's handlers and are the schemas any other Oshun domain
would use to call Freya.

### Applications (implemented over in-memory state)

Five apps compose the libraries into workflows. They depend on capability
libraries but never on each other. `apps/freya/api` is the surface
(`schemas.ts`, `middleware.ts`, the `*-routes.ts` handlers, `graphql-schema.ts`,
`websocket.ts`, `grpc-services.ts`, `event-bus.ts`, `openapi-spec.ts`).
`studio`, `shop`, `retail`, and `manufacturing` are the design, DTC, store-ops,
and factory-floor apps. The API enforces real RBAC in `middleware.ts`
(`ROLE_PERMISSIONS` for `customer`/`associate`/`manager`/`admin`, `:own`
ownership checks, three rate-limit buckets) and publishes a genuine OpenAPI
3.1.0 document (`FREYA_OPENAPI_SPEC` in `openapi-spec.ts`, with `components`,
`paths`, and `$ref`-linked schemas).

## Component and Data Flow

```mermaid
flowchart TD
  subgraph Apps["apps/freya/* (in-memory Map stores)"]
    Studio[studio]
    Shop[shop]
    Retail[retail-app]
    Mfg[manufacturing-app]
    API[api: REST + RBAC + OpenAPI]
  end

  subgraph Caps["libs/freya/* capability libraries"]
    Fashion[fashion]
    Ecom[ecommerce]
    Manu[manufacturing]
    RetailLib[retail]
    Beauty[beauty]
    Textiles[textiles]
    Brand[brand]
    Jewelry[jewelry]
    Fin[financials]
    Market[market-intel]
    Sota[sota]
    Facades[11 BU facade libs]
  end

  subgraph Foundation["foundation"]
    Core["@freya/core: types + algorithms"]
    Catalog["FREYA_BUSINESS_UNIT_CATALOG"]
  end

  subgraph Defined["defined-as-code, NOT wired"]
    DB["@freya/db: 24 Drizzle tables + Redis/S3 cfg"]
    Bus["event-bus: CloudEvents taxonomy + topic map"]
    Grpc["grpc-services: contracts + .proto generator"]
    Conn["connectors: cross-domain brief builders"]
  end

  Studio --> Fashion & Beauty & Textiles & Brand
  Shop --> Ecom
  Retail --> RetailLib
  Mfg --> Manu
  API --> Ecom & Manu & Fin & Market
  Caps --> Core
  Facades --> Catalog
  Catalog --- Core
  Sota -. opt-in enrichment .-> Caps
  Caps -. would persist via .-> DB
  API -. would emit via .-> Bus
  API -. would call .-> Grpc
  Caps -. would dispatch via .-> Conn
  Conn -. roadmap .-> Aglaea[(Aglaea & peer domains)]
```

The intended operational cycle, much of which is implemented in-process today:

1. **Creation.** Designers/operators create product, material, collection, and
   manufacturing records (studio + API). Objects get branded IDs and enter the
   product/design lifecycle state machines.
2. **Enrichment.** `brand`, `market-intel`, and `sota` attach trend, campaign,
   demand, and pricing signals. (`sota` is opt-in; the deterministic core stands
   alone without it.)
3. **Selling.** `ecommerce` and `retail` publish only `Active` products and run
   carts, checkout, payments, and omnichannel inventory against a shared stock
   model.
4. **Manufacturing and quality.** `manufacturing` consumes orders and closes the
   production loop with status updates and AQL-gated inspection; a failed
   inspection holds the order until rework re-passes.
5. **Planning.** `financials` and `market-intel` aggregate into per-unit P&L,
   brand health, and supplier-risk views that feed the next design cycle.

## Three Order State Machines (a deliberate, real distinction)

Freya models orders at three different surfaces, each with its own lifecycle —
this is intentional, and a frequent source of confusion:

- **Supply-chain `OrderStatus`** (`@freya/core/supply-chain-types.ts`):
  `draft → placed → confirmed → in_production → quality_check → ready → shipped → delivered`,
  with `cancelled`. This is the manufacturing/procurement order.
- **DTC fulfillment** (`@freya/ecommerce/catalog-inventory.ts`): a
  fourteen-state machine
  `placed → payment_confirmed → processing → picked → packed → shipped → out_for_delivery → delivered`,
  plus return/refund states.
- **API customer order** (`apps/freya/api/order-routes.ts`): a compact
  `PLACED → CONFIRMED → SHIPPED → DELIVERED → RETURNED`, `CANCELLED` from
  `PLACED`/`CONFIRMED`. The handler validates transitions against a
  `validTransitions` table and rejects illegal moves.

These are not duplicates to be merged; they describe genuinely different
processes (procuring stock, fulfilling a parcel, tracking a customer order).

## Cross-Domain Boundaries

- **Freya vs. Aglaea.** Supply-side vs. consumer-side. Freya _publishes_
  products, sizing, inventory, campaigns, and provenance; Aglaea _consumes_
  them. Freya never makes a per-consumer styling decision; no Freya library
  imports Aglaea.
- **Freya vs. Cybele.** Physical stores and factories are Cybele's; Freya models
  the _business operations_ inside them. The Cybele connector builds
  store-design and factory-construction briefs.
- **Freya vs. Brigid.** Factory-automation machinery is Brigid's; Freya models
  execution and inspection at the batch/order level.
- **Freya vs. Saraswati.** Logistics and IoT for smart/tech products are
  Saraswati's; Freya consumes them when a product embeds devices.
- **Freya vs. Maat / Asase.** Freya _produces_ brand/market/financial analytics
  that Maat aggregates; Freya _consumes_ Asase for natural-ingredient
  traceability. All five integrations route through `@freya/connectors`.

Today these boundaries are honored structurally (the connector is the only place
that would reach across), but the connectors compute briefs locally rather than
calling the peer domains.

## Invariants, Failure Modes, and Extension Points

**Invariants.** Capability libraries depend only on `@freya/core`, never on each
other (verified: the libraries import `@freya/core` and not sibling capability
packages). The business-unit catalog must hold exactly nineteen unique entries
(`assertCompleteFreyaBusinessUnitCatalog`). Fiber and INCI compositions must sum
to 100% ±0.5. Product/material provenance is meant to be append-only. State
machines reject illegal transitions by throwing rather than silently coercing.

**Failure modes.** Guards fail loud with typed `FreyaError` subclasses
(`FREYA_VALIDATION_ERROR`, `FREYA_NOT_FOUND`, `FREYA_BUSINESS_RULE_VIOLATION`)
rather than returning sentinels. The largest _systemic_ gap is durability: every
store is an in-process `Map`, so state is lost on restart and is not shared
across processes — `resetOrderStore()`-style helpers exist precisely because
state is in-memory. Idempotency (a stated commerce requirement) is partially
modeled via reservation tokens and TTL soft-locks but is not yet enforced
through a transactional database.

**Extension points.** Adding a business unit means appending a profile to
`FREYA_BUSINESS_UNIT_CATALOG`, bumping the assertion, and adding a facade (or,
for a rich unit, a multi-module library) over `@freya/core`. New events are
added to the `FreyaEventType` union and routed in `FREYA_EVENT_TOPICS`. New gRPC
methods are appended to `FREYA_GRPC_SERVICES` and flow through
`generateProtoStub`. The clearest forward path for the domain is the wiring
phase: making the capability libraries import `@freya/db`, persisting through
the existing Drizzle schema, and standing up the already-specified
Kafka/gRPC/Redis surfaces.

## Verification Expectations

Each library and app ships a Vitest suite (`*.spec.ts`). Because state machines,
inventory accounting, AQL inspection, and financial calculations are the
domain's correctness core, their tests assert specific computed values against
known-correct answers, not just shape. Commerce, payment, inventory,
manufacturing, and customer-data changes additionally warrant contract
(`@freya/contracts`) and schema (`@freya/db`) regression coverage so the typed
boundary and the persistence definition stay consistent — even while the running
engines remain in-memory. Run with `pnpm nx test <project>` (or `npx vitest run`
directly from a library directory when Nx is contended by worktrees).
