Catalog module
AI-powered product information management, semantic search, configurable taxonomy, supplier shortlisting. Absorbs the Stella Catalog feature set — taxonomy is configurable per tenant (UNSPSC, ETIM, NATO FSC, CPV, or custom).
- Source:
apps/catalog/ - Schema:
catalog - Project Tracker prefix:
CAT-* - Hosting: sub-zone behind Directory; production basePath is
/catalog/*(rewritten from the root zone).
Current architecture
The Catalog that ships today is a single Next.js App Router application at apps/catalog/, following the same three-layer split as every other Constellation module:
API Route (src/app/api/) → Tool (src/server/tools/) → Service (src/server/services/) → Repository (src/server/repositories/)
Routes are thin adapters that call tools; tools express business capabilities and call services and policies; services hold the domain logic (including the AI orchestration for description generation) and call repositories; repositories are persistence-only over the catalog Postgres schema (Prisma, plus raw SQL for ltree / pgvector operations). Automation — event handlers and the lazy embedding-fill workflow — goes through tools or services, never repositories directly. The full picture is in §2 Component diagram and §7 Layers + call direction.
This is not the standalone NestJS, tri-layer service described in the Catalog architecture (v3) design page — that was a greenfield design for Stella Catalog that was never built. Whenever the two disagree, this page (the shipped Next.js app) is authoritative.
1. Purpose
Catalog is where suppliers describe what they offer and buyers find it. Suppliers publish products, services, and capabilities; buyers search by meaning rather than exact keywords, browse a familiar industry classification, and turn results into a curated shortlist that feeds the delivery work in Project Tracker.
Key capabilities
- Semantic search — queries match by meaning (vector embeddings), not just keywords
- Configurable industry taxonomy per tenant: UNSPSC, ETIM, NATO FSC, CPV, or custom
- Shortlists: curated, lifecycle-managed selections of catalogue entries
- AI-generated entry descriptions
Who uses it
- Suppliers publishing and maintaining their catalogue entries
- Agency buyers and procurement officers searching and shortlisting
- The planned Procurement module, which will build RFQ flows on top of shortlists
For product context, see What is Constellation?. The rest of this page is the technical reference.
Catalog is the platform's product knowledge layer. It owns the catalogue entries (products, services, capabilities), the configurable industry taxonomy that classifies them, and the shortlisting / supplier-matching primitives that downstream procurement modules rely on. Two technical pillars set Catalog apart: pgvector for semantic-search embeddings and ltree for hierarchical taxonomy paths. AI-generated descriptions (via @constellation-platform/ai-core) are an explicit feature of the module rather than a layer concern.
2. Component diagram (C4 L3)
Call direction is strict: API Route → Tool → Service → Repository. Routes never call services or repositories directly; workflows and event handlers go through tools or services. See route-wrapping.
3. Schema
catalog — owned by Catalog. Postgres extensions required: pgvector (embeddings on catalog_entries), ltree (path-based taxonomy hierarchies), pg_trgm (fuzzy text matching). RLS is enforced on every tenant-scoped table via app.tenant_id. Cross-module reads of identity.* are read-only via raw SQL.
4. Entities
| Aggregate | Purpose |
|---|---|
catalog_entries | Products / services / capabilities. Lifecycle: DRAFT → PUBLISHED → ARCHIVED. Carries pgvector embeddings and AI-generated descriptions. |
catalog_taxonomy | Configurable taxonomy — LTREE path enables sub-tree queries. Per-tenant choice of UNSPSC / ETIM / NATO FSC / CPV / custom. |
shortlists | Curated subsets of catalogue entries — feeds Procurement RFQ flows. Lifecycle: ACTIVE → ARCHIVED. |
shortlist_entries | Membership join between a shortlist and its catalogue entries. |
5. Domain events
Published from src/server/events/catalog.events.ts via the transactional outbox under the catalog.* namespace. Full payload schemas are in @constellation/contracts and indexed in the Domain events index.
catalog.entry.created, catalog.entry.updated, catalog.entry.deleted, catalog.entry.status_changed, catalog.taxonomy.created, catalog.taxonomy.updated, catalog.taxonomy.moved, catalog.taxonomy.deleted, catalog.shortlist.created, catalog.shortlist.updated, catalog.shortlist.deleted, catalog.shortlist.archived, catalog.shortlist.entry_added, catalog.shortlist.entry_removed.
6. Public API
The Catalog API reference is not yet wired into this site — adding Zod→OpenAPI to apps/catalog/ is tracked under INF-26. Illustrative routes:
POST /api/catalog-entries— create a draft catalogue entry; AI description and embedding are generated asynchronously.POST /api/search— semantic search across published entries (uses pgvector ANN over the embeddings column).POST /api/shortlists— create a shortlist from search results or a manual selection.
7. Layers + call direction
| Layer | Path | May import from |
|---|---|---|
| API Routes | src/app/api/ | Tools only |
| Tools | src/server/tools/ | Services, Policies, Events |
| Services | src/server/services/ | Repositories, Policies, @constellation-platform/{db,ai-core,ai-embeddings} |
| Repositories | src/server/repositories/ | @constellation-platform/db (Prisma + raw SQL for ltree / vector ops) |
| Policies | src/server/policies/ | @constellation-platform/auth-core |
| Workflows | src/server/workflows/ | Tools or Services (never repositories) |
| Events | src/server/events/ | @constellation-platform/events publish() + outbox |
Enforced at PR time by scripts/check-route-wrapping.ts — invoked via npm run check:routes, which scans apps/catalog/src/app/api and apps/directory/src/app/api. Every withAuth must be paired with withTenantAuth. Plus ESLint import boundaries.
8. Code entry points
- Tool:
apps/catalog/src/server/tools/catalog-entry.tools.ts— entry lifecycle operations. - Service:
apps/catalog/src/server/services/catalog-entry.service.ts— domain invariants and AI orchestration. - AI service:
apps/catalog/src/server/services/description-generator.service.ts— wraps@constellation-platform/ai-corewith budget + audit. - Repository:
apps/catalog/src/server/repositories/catalog-entry.repository.ts. - Events module:
apps/catalog/src/server/events/catalog.events.ts.
9. Known exceptions / pitfalls
pgvectorandltreeare required. Local dev brings them up viascripts/db-init.sql— verify with\dxin psql before running migrations.- AI calls always go through
@constellation-platform/ai-core. Don't import provider SDKs (@anthropic-ai/sdk,openai) directly anywhere outside the platform packages — that's how the budget and audit interceptors stay in the request path. Seeprovider-abstraction. - Embeddings are generated lazily. A new entry doesn't get an embedding inside the create transaction — there's a workflow that picks up entries with
embedding IS NULLand fills them in. Search for an entry that's seconds-old returns no result; that's expected. - Optional clearance / classification gating. Tenants that need defence-style access control enable per-entry classification + per-user clearance. Off by default; switching it on changes the search filter shape — see the catalog spec.
- Cross-module reads are read-only via raw SQL. Don't define Prisma cross-schema relations to
identity.*— use@constellation-platform/dbraw queries.
See also
- Catalog architecture (v3) — merged conceptual model, technology stack, platform alignment.
- Catalog product specification — Stella Catalog technical spec (data model, AI enrichment, syndication).
- Architecture overview — C4 L1 / L2 view of how Catalog sits inside the platform.
- Domain events index — full payload schemas for the events listed in §5.