Skip to content

Architecture

1. System Context (C4 L1)

Commerce does not push to sister services directly. The integration seam is CDC (Debezium tails the Postgres WAL). See ADR-0002.

2. Container View (C4 L2)

3. Component View (C4 L3) — Internal Layering

LayerResponsibility
RoutesBase paths in RestPaths
ControllersAuth gate (jwt/basic), role-based filtering, DTO mapping
ServicesAggregate transactions, EventBus emission, encryption
RepositoriesDrizzle queries, soft-delete
ComponentsKafka producer, BullMQ queue/worker, EventBus, WebSocket, search/asset, two Redis

4. State Machines Index

EntityStatesDiagram
ProductDRAFT, ACTIVATED, DEACTIVATED, ARCHIVED
ProductVariantDRAFT, ACTIVATED, DEACTIVATED, ARCHIVED
Merchant onboardingper-step boolean flags

Product / ProductVariant status

ARCHIVED is terminal. Statuses are reused from @venizia/ignis (ProductStatuses / ProductVariantStatuses). A DRAFT value also exists in the enum.

ProductVariant.type (structural discriminator)

Not a lifecycle — a type that drives stocking & bundling behavior. Source: core/.../product-variant/constants.ts.

TypeStockableBOMNotes
STORABLEphysical, stocked (default)
CONSUMABLEphysical, not stocked
SERVICEno physical goods
KITexplodes at sale through Material
COMBOvirtual; explodes at sale through component ProductVariant
MANUFACTUREDproduce-to-stock

See ADR-0003.

Merchant onboarding steps

MerchantOnboardingSteps flags live in Merchant.metadata.onboarding. ProductAggregateCreatedListener.handleMarkOnboardingStepComplete marks the PRODUCT step on first product create.

5. Runtime Scenarios

5.1 Product Aggregate Create + Multi-Merchant Sync

The aggregate transaction commits before the EventBus emit — sync is best-effort/async. See ADR-0001.

5.2 Organizer Onboarding (synchronous)

No COMMERCE_INITIALIZED_* fan-out queue — downstream setup is CDC-driven.

5.3 Merchant Tax Info → TaxInfo (CDC)

MST/e-invoice is stored in Merchant.metadata.tax (not a column). TaxInfo is the authoritative downstream read source. See merchant tax-info flow.

5.4 Encrypted Provider Integration

6. Crosscutting Concerns

ConcernHow this service handles it
AuthNJWT (ES256, JWKS from identity); BASIC for service-to-service. VerifierApplication.
AuthZRole-based filtering via useRequestContext() + AppFixedRoles; PolicyDefinition Casbin GROUP rules cached in authorization Redis
i18njsonb { en, vi } via i18n() columns
LoggingIGNIS structured (key: %s)
EncryptionEncryptService AES-256-GCM with APP_ENV_APPLICATION_SECRET; getDecryptedCredential internal-only
Integration seamCDC (Debezium WAL) — producer-only Kafka, no app-level produce
Async replicationIn-process EventBus → BullMQ SYNC_PRODUCT_QUEUE → worker
Soft-deleteSoftDeletableRepository (deletedAt) on all entities
IDsSnowflake via IdGenerator, worker 2; slugs auto-generated if absent
Dual modeApplicationRoles.API / WORKER; CDC consumer + workers only in WORKER role

Proprietary and Confidential. Unauthorized copying, distribution, or use of this software is strictly prohibited.