Skip to main content
Version: v1.0.0(int)

Layer 2 — Components

Stateless, in-process modules — one per domain — sitting between the database and the L3 workflows. Each component is a thin DB-access API for the tables it owns.

Principles

  1. One component per domain. Thin DB-access only. No constraints, no cross-component calls, no business rules. Constraints belong in L3.
  2. Every op accepts a connection. Components don't import a pool. The conn enters as a parameter.
  3. Stateless. No module-level caches, no singletons, no memoization. Same (conn, args) → same SQL → same result.
  4. One table → one owner. A component can own many tables; a table belongs to exactly one component. No other component reads or writes it directly.

Tenant context

The request boundary sets SET LOCAL app.org_id = $1 on the conn before passing it down. L2 SQL reads the tenant via current_setting('app.org_id')::uuid and still carries an explicit WHERE org_id = $1 clause. No RLS.

Connection handling

L2 ops accept a Queryable (just .query()Pool, PoolClient, or a test fake all satisfy it). L2 never opens transactions; L3 owns the unit of work.

The request boundary uses two helpers:

  • withConnection(pool, orgId, fn) — reads. Acquires a conn, applies SET LOCAL app.org_id, releases.
  • withTransaction(pool, orgId, fn) — writes. Same + BEGIN / COMMIT / ROLLBACK.

Response shape

L2 maps SQL rows to idiomatic TS at the boundary:

  • Keys are camelCase.
  • NULLnull, never undefined.
  • BIGINT columns → strings (precision-safe).
  • DATE columns → 'YYYY-MM-DD' strings.
  • TIMESTAMPTZ → JS Date.
  • JSON / JSONB → parsed.

Cross-domain reads

Components don't call each other. When a workflow needs data spanning two components, L3 calls both L2 ops and merges in memory — two batched queries instead of one cross-domain JOIN.

Ledger ownership

Materials and finished-goods ledger writes are exclusively the ledger components' API. PO receipts, WO receipts, and transfers call into MaterialsInventory / FinishedGoodsInventory rather than inserting directly.

Components

ComponentSchemaOwns
Platformplatformusers, organization_users, organizations, user_invitations, api_tokens
OrganizationSettingsorgorganization_settings
Customersorgcustomers, customer_types, customer_segments, brokers
Vendorsorgvendors
Productsorgskus, sku_types
Materialsorgmaterials, material_types
Locationsorglocations
Contactsorgcontacts, entity_contacts
PurchaseOrdersorgpurchase_orders, po_items, po_receipts, po_receipt_items
WorkOrdersorgwork_orders, wo_items, work_order_inputs, wo_receipts, wo_receipt_items
SalesOrdersorgsales_orders, so_items, so_timeline_notes, order_channels
BOMorgboms, bom_items
MaterialsInventoryorgmaterials_inventory_ledger
FinishedGoodsInventoryorgfinished_goods_inventory_ledger
Documentsorgdocuments