domain

Design Management

Bounded context for designer-curated artifacts in the design base. Owns ProjectCollection, collection items, ProjectProductVariant, product references, PartType, and Slides.com deck jobs. Product catalog records stay in product-marketplace.

DomainStatus: Implemented in api-v1Airtable-backed

Overview

Design Management owns designer-curated artifacts in apps/api-v1/src/modules/design-management. A ProjectCollection (short name: Collection) is a designer-created collection or favorites list of products for a project or design workflow. Catalog records remain owned by Product Marketplace; this module references them through ProductReference and read ports.

The module follows api-v1 hexagonal structure. Collections, items, part types, and deck jobs persist in the Airtable design base (appDr2xVXbdJ5b08R, AIRTABLE_DESIGN_BASE_ID). Portal publish writes a Postgres snapshot via PrismaPortalSyncAdapter; the client portal reads that snapshot from api-v2. There is no nested System: api-v1 and api-v2 already collaborate under Backend Platform.

This domain does not send domain events. Earlier event types were removed; use cases return application results. Do not invent events.

Implementation Status

Status: Implemented in api-v1

Portal reads already live on api-v2 (portal-read + GET /api/v2/client-portal/...). That is a BFF over published snapshots, not a replacement for Airtable collection curation. See Backend Platform — Portal read path.

Aggregates and supporting types

ConceptPersistence todayBadge
ProjectProductVariantAirtable project_product_variants (ADR-0083); referenced by PDG product assignmentImplemented in api-v1
ProjectCollectionAirtable user_product_collections; portal snapshot in PostgresImplemented in api-v1
ProjectCollectionItemAirtable user_product_collection_itemsImplemented in api-v1
ProductReferenceValue object on each item (PM catalog product / variant / MTO id — not a Design product_variants rec)Implemented in api-v1
PartTypeAirtable part types, generated from an MTO templateImplemented in api-v1
ProductCollectionDeckJobAirtable deck jobs; Slides.com is an external publisherImplemented in api-v1

ProjectCollection is the aggregate root. Collection item add/remove/select and portal publish/unpublish run on that aggregate. PartType and ProductCollectionDeckJob are separate persisted types in this module.

Boundary with Product Marketplace

Product browse, search, filter, refresh/reload/delete, image publishing, onboarding, and dimension migration stay in product-marketplace. Manufacturer or vendor product lines are VendorProductCollection, not ProjectCollection.

This module looks up MTO templates through AirtableMtoTemplateAdapter. Product Marketplace Add to Project creates Project Product Variant rows and the default Project Collection. Graph product assignment is owned by project-delivery-graph and references variant domain_id. Extra collection membership uses POST /project-collections/:id/add-items. This module does not own catalog records.

Portal publish

Publish Collection To Portal is the write path:

  1. api-v1 PublishToPortalUseCase / UnpublishFromPortalUseCase (ForPublishingToPortal, ForUnpublishingFromPortal)
  2. Aggregate publishToPortal / unpublishFromPortal (draftpublished)
  3. ForSyncingPortalData implemented by PrismaPortalSyncAdapter (upsert or unpublish in Postgres)
  4. Airtable save of collection portal fields

Reads stay on api-v2: apps/api-v2/src/modules/portal-read and GET /api/v2/client-portal/projects/:projectId/project-collections. Documented on Backend Platform. Do not treat this split as the product-marketplace or BIM migration.

Slides.com

ProductCollectionDeckJob is a publication job that calls Slides.com through SlidesComProductCollectionDeckAdapter. Slides.com is an external publisher, not a catalog service. Do not add a Slides.com EventCatalog service.

Primary Code References

  • Module root: apps/api-v1/src/modules/design-management
  • Glossary: apps/api-v1/src/modules/design-management/GLOSSARY.md
  • Composition: apps/api-v1/src/modules/design-management/composition/design-management.composition.ts
  • REST: apps/api-v1/src/modules/design-management/adapters/inbound/rest/design
  • Airtable base id: appDr2xVXbdJ5b08R (AIRTABLE_DESIGN_BASE_ID)
  • Portal write adapter: apps/api-v1/src/modules/design-management/adapters/outbound/persistence/portal/PrismaPortalSyncAdapter.ts

HTTP surface

Canonical mount: /api/v1/design.

MethodPathBehavior
POST/project-collections/:id/add-itemsAdd existing collection items to a collection
POST/project-collections/:id/remove-itemsSoft-delete items on the aggregate
POST/project-collections/:id/select-itemsScript-flag select; propagates to active items
POST/project-collections/deleteDestroy collections and/or items in Airtable
POST/projects/:projectId/actions/add-variantsAdd catalog variants to {projectName}-default
POST/projects/:projectId/actions/publish-to-portalPublish collections to the portal snapshot
POST/projects/:projectId/actions/unpublish-from-portalMove collections back to draft
POST/product-mtos/:productMtoId/generate-partMaterialize a PartType from an MTO template
POST/project-collections/:productCollectionId/publish-slidesSync Slides.com deck publish
POST/slides/publish-collectionAsync deck job (ProductCollectionDeckJob)
GET/slides/jobs/:jobId/statusDeck job status
GET/slides/launchBlind Slides.com launcher

There is no colocated *.openapi.ts for this module yet — do not invent OpenAPI operation ids. Legacy /api/app/design/* redirects to /api/v1/design/*.

Open Questions

  • When collection curation moves to api-v2, which Airtable tables remain a steward write model versus a read model?
  • Whether a domain event publisher should be reintroduced for portal publish. Today there is none; do not invent events until a dispatcher exists.