Commerce Service
@nx/commerce is the central catalog and tenant service: it owns products, variants, options, categories, merchants, organizers, sale channels, devices, receipt templates and encrypted provider integrations. It is the CDC source of truth — Debezium tails its Postgres WAL and many sister services (search, pricing, taxation, invoice, inventory) seed themselves from it. It registers a Kafka producer only and replicates products across merchants via an in-process EventBus + BullMQ queue.
1. Quick Reference
| Property | Value |
|---|---|
| Package | @nx/commerce |
| Code | SVC-00020-COMMERCE |
| Type | Microservice |
| Runtime | Bun |
| Base Class | VerifierApplication (JWKS-based JWT verification) |
| Location | packages/commerce |
| Base Path | /v1/api/commerce |
| Dev Port | 31020 |
| Container Port | 3000 (external 31020) |
| Snowflake ID | 2 |
| DB Schema | public (+ allocation) — schemas owned in @nx/core |
| Binding Namespace | @nx/commerce |
| Live OpenAPI | /v1/api/commerce/doc/openapi.json |
2. Purpose & Scope
| Included | Excluded |
|---|---|
| Product / variant / option catalog (aggregate CRUD) | Dynamic pricing — fares/rules (delegated to @nx/pricing) |
| Category hierarchy (SYSTEM + custom) | Tax rule provisioning (@nx/taxation) |
| Merchant + organizer (tenant) management & onboarding | Stock levels / InventoryItem (delegated to @nx/inventory) |
Sale-channel config + SaleChannelProduct links | Order / checkout lifecycle (delegated to @nx/sale) |
| Device, receipt template, setting, discrimination type | E-invoice issuance (delegated to @nx/invoice) |
| Encrypted provider-integration credentials (AES-256-GCM) | Search indexing engine (Typesense — synced via @nx/search CDC) |
| Background multi-merchant product replication (EventBus → BullMQ) | Direct push to inventory (CDC-driven instead) |
Merchant tax info capture (Merchant.metadata.tax) → CDC → TaxInfo | |
| WebSocket emitter for real-time merchant events |
3. Tech Stack
External:
| Library | Purpose |
|---|---|
@venizia/ignis | IoC container, DI, BaseService/Controller, ControllerFactory |
@venizia/ignis-helpers | Logger, KafkaProducerHelper, BullMQHelper, WebSocketEmitter, AES |
@platformatic/kafka | Kafka client (producer-only) |
eventemitter3 | In-process EventBus for product aggregate events |
hono + @hono/zod-openapi | HTTP server + OpenAPI generation |
@scalar/hono-api-reference | OpenAPI explorer at /doc |
drizzle-orm + pg | DB access via PostgresCoreDataSource |
minio | Object storage (via @nx/asset) |
typesense | Search engine client (via @nx/search) |
Internal:
| Package | Purpose |
|---|---|
@nx/core | Schemas, repositories, VerifierApplication, CDCKafkaTopics, status/type enums |
@nx/asset | ApplicationAssetComponent — Minio media + MetaLinks |
@nx/search | Search + embedding + Typesense + CDC consumer components |
@nx/mq-sms | Declared dependency; not imported in src/ |
4. Project Structure
packages/commerce/
├── src/
│ ├── application.ts # VerifierApplication subclass
│ ├── index.ts / migrate.ts # bootstrap entry points
│ ├── common/
│ │ ├── constants.ts # ApplicationRoles (api/worker)
│ │ ├── keys.ts # BindingKeys (Kafka/BullMQ/EventBus)
│ │ ├── event.ts # EVENT_EMITTER product event keys
│ │ ├── rest-paths.ts # RestPaths
│ │ └── environments.ts # BullMQ Redis env keys
│ ├── components/
│ │ ├── kafka/ # ApplicationKafkaComponent (producer-only)
│ │ ├── redis/ # BullMQ Redis connection
│ │ ├── queues/ # SYNC_PRODUCT_QUEUE
│ │ ├── event-bus/ # EventEmitter3 bus + registry
│ │ ├── workers/ # SyncProductWorker + WorkerComponent
│ │ └── websocket/ # CommerceSocketEventService
│ ├── controllers/ # 18 folders → 17 registered controllers
│ ├── datasources/ # PostgresCoreDataSource
│ ├── events/ # emitters + product aggregate listeners
│ ├── services/ # incl. services/product, services/allocation
│ ├── migrations/processes/ # 9 seed/backfill processes
│ └── models/ # zod request/response schemas
├── package.json
└── tsconfig.json5. Architecture
Detail: see Architecture.
6. Domain Snapshot
Full ERD + per-entity tables: see Domain Model.
7. Surface Summary
REST controllers — full reference rendered live from /v1/api/commerce/doc/openapi.json (Scalar viewer at /doc, gateway portal). 17 registered controllers; all authenticate via strategies: ['jwt', 'basic'].
| Controller | Base path | Notes |
|---|---|---|
ProductController | /products | + /aggregate create/update |
ProductVariantController | /product-variants | + /aggregate create/update |
ProductOptionController | /product-options | option definitions |
ProductOptionValueController | /product-option-values | option values |
ProductVariantOptionController | /product-variant-options | variant↔option selection |
CategoryController | /categories | SYSTEM + custom; no CategoryService |
MerchantController | /merchants | + aggregate, footer-summary, deletion-policy |
OrganizerController | /organizers | + on-boarding (synchronous) |
SaleChannelController | /sale-channels | identifier resolution |
ConfigurationController | /configurations | + /provider-integrations (encrypted) |
DeviceController | /devices | POS device registry |
DiscriminationTypeController | /discrimination-types | grouping taxonomy |
ReceiptTemplateController | /receipt-templates | print templates |
SettingController | /settings | polymorphic settings |
AllocationLayoutController | /allocation-layouts | floor plans |
AllocationZoneController | /allocation-zones | zones/sections |
AllocationUnitController | /allocation-units | tables/seats |
SaleChannelInventoryLocationController+SaleChannelInventoryLocationServiceexist undersrc/but are not registered.
Async surface — full reference in API Events:
| Direction | Channel | Count |
|---|---|---|
| Outbound | CDC (Debezium) | 9+ tables consumed by search/pricing/taxation/invoice/inventory |
| Internal | EventBus (eventemitter3) | 2 events (product.aggregate.{created,updated}) |
| Internal | BullMQ | 1 queue (SYNC_PRODUCT_QUEUE) × create/update jobs |
| Outbound | WebSocket | merchant real-time emissions (emitter registered) |
| Outbound | Kafka produce | producer bound; no .send() call sites in src/ |
8. Components
Registered in application.ts:configureComponents() (in order):
| Component | File / source | Purpose |
|---|---|---|
ApplicationSearchComponent | @nx/search | Typesense search integration |
ApplicationEmbeddingConfigurationComponent | @nx/search | Embedding config for semantic search |
TypesenseSearchEngineComponent | @nx/search | Typesense engine wiring |
ApplicationCdcComponent | @nx/search | WORKER role only — Debezium CDC → Typesense |
ApplicationAssetComponent | @nx/asset | Minio-backed media + MetaLinks |
useCacheRedis(...) | core helper | Cache Redis (also authorization Redis) |
RedisComponent | components/redis/ | Separate BullMQ Redis (single/cluster) |
QueueComponent | components/queues/ | Creates SYNC_PRODUCT_QUEUE |
EventBusComponent | components/event-bus/ | EventEmitter3 bus + product aggregate listeners |
WorkerComponent | components/workers/ | Registers workers from APP_ENV_WORKERS |
ApplicationKafkaComponent | components/kafka/ | Kafka producer initializer (no .send() in src/) |
ApplicationWebSocketComponent | components/websocket/ | WebSocketEmitter for real-time events |
Two Redis connections (cache + BullMQ).
APPLICATION_KAFKA_CONSUMERkey is declared but no consumer is wired.
9. Services
Registered in application.ts:configureServices():
| Service | One-liner |
|---|---|
ProductService | Product read + identifier resolution; delegates aggregate to create/update services |
ProductCreateService / ProductUpdateService | Aggregate transaction logic; emit EventBus product events |
ProductCreateSyncService / ProductUpdateSyncService | syncToAdditionalMerchants() (BullMQ worker) |
ProductVariantService | Variant aggregate CRUD (catalog only — no pricing injection) |
ProductOptionService | Option / option-value management |
MerchantService | Merchant aggregate, tax→metadata, footer-summary cache |
OrganizerService | onBoarding() — organizer + merchant + policies + default channel |
CategoryService | SYSTEM/custom category logic |
SaleChannelService | Identifier-based lookup |
ConfigurationService | System configs + encrypted provider integrations |
EncryptService | AES-256-GCM encrypt/decrypt (internal) |
DeletionPolicyService | Per-merchant strict deletion enforcement |
AllocationLayoutService / AllocationZoneService (+ Create/Update) | Seat/zone allocation layouts |
ReceiptTemplateService | Receipt template CRUD |
WebhookDispatcherService | Re-exported from @nx/core |
SaleChannelInventoryLocationServiceexists but is not registered.
10. Repositories
Re-exported from @nx/core and registered in configureRepositories(). Includes commerce-owned tables (Product, ProductVariant, ProductInfo, ProductIdentifier, ProductOption(Value), ProductVariantOption, ProductVariantMapping, ProductBundler, ProductCategory, Category, Merchant, Organizer, SaleChannel(Product), Configuration, Device, DiscriminationType, ReceiptTemplate, Setting, MetaLink) plus cross-package reads (SaleOrder(Item), Inventory*, Invoice, Material, Fare(Set), Tax*, FinanceAccount, TaxInfo, Permission, Role, PolicyDefinition, Notification, WebhookConfig, Customer).
Per convention, no thin wrapper repos — custom methods (e.g.
MerchantRepository.markOnboardingStepComplete,SaleOrderRepository.sumWindowCompletedOrders,InventoryStockRepository.getOverview) earn their place via non-trivial logic.
11. Entry Points
| File | Purpose |
|---|---|
src/index.ts | Service entry → bootstrapApplication() |
src/migrate.ts | Migration entry → bootstrapMigration() |
src/application.ts | Application extends VerifierApplication |
12. Configuration
Env vars + seeded data: see Configuration.
13. Operations
Deployment + observability + security + runbook: see Operations.
14. Related Pages
Concepts — why/how:
- Architecture
- Domain Model
- Integration — CDC seam + sister-service contracts
Reference — lookup:
- API Events
- Configuration
- Operations
- REST endpoints — live OpenAPI at
/v1/api/commerce/doc/openapi.json
Features — deep dives:
Decisions: