Shop It Docs
Developer Resourcesinventory

Inventory Module Backend Documentation

Inventory runtime architecture, stock mutation rules, queue processing behavior, and operational constraints.

Inventory Module - Backend Documentation

1. Backend Scope and Boundaries

Inventory backend owns:

  • physical stock state in product_inventory
  • stock mutation math and invariants (reserve, finalize, release, adjust, sync)
  • admin inventory read/mutation HTTP surface
  • BullMQ processors for stock lifecycle jobs

Inventory does not own:

  • checkout/cart business eligibility
  • payment gateway execution
  • order lifecycle state machine
  • outbox orchestration policy (owned by order processing runtime)

2. Module Composition (Aggregate + Leaf)

InventoryModule composes:

  • InventorySharedModule
  • InventoryAdminModule
  • InventoryProcessorModule

Ownership model:

  • InventorySharedModule exports InventoryService
  • InventoryAdminModule owns admin controller/service + DTO mapping
  • InventoryProcessorModule owns BullMQ processors and job handlers

Application registration:

  • InventoryModule is imported in AppModule and available in full runtime profile

3. Data Model (Drizzle / PostgreSQL)

Primary table:

  • product_inventory

Column-level summary:

ColumnTypeConstraintNotes
idserialPKinventory row id
product_idintunique FK -> product.idone row per product
total_quantityintdefault 0total tracked quantity
reserved_quantityintdefault 0reserved pending units
available_quantityintdefault 0sellable units
low_stock_thresholdintdefault 5low-stock indicator threshold
track_inventorybooleandefault truewhether inventory checks apply
allow_oversellbooleandefault falseoversell policy
created_attimestamptznot nullrow creation time
updated_attimestamptznot nullmutation time

Indexes/constraints:

  • product_inventory_product_id_idx
  • check: total_quantity >= 0
  • check: reserved_quantity >= 0
  • check: available_quantity >= 0

Schema references:

  • packages/db/src/schema/inventory/product-inventory.ts
  • packages/db/src/schema/inventory/index.ts
  • exported via packages/db/src/schema/index.ts

4. Service Contracts and Mutation Semantics

Core service: InventoryService

Public methods:

  • reserveStock(payload)
  • finalizeStock(payload)
  • releaseStock(payload)
  • adjustStock(productId, dto)
  • syncStock(productId?)
  • getByProductId(productId)
  • getLowStock(threshold?)

4.1 Reserve (reserveStock)

Behavior:

  1. opens transaction
  2. row-locks inventory row (FOR UPDATE) for productId
  3. if row missing: auto-creates zeroed row and returns success: false with INVENTORY_CREATED_AUTOMATICALLY
  4. when trackInventory=true && allowOversell=false, rejects if available < quantity
  5. updates:
    • available = available - quantity
    • reserved = reserved + quantity

Error behavior:

  • INVENTORY_INSUFFICIENT_STOCK
  • INVENTORY_INVALID_OPERATION

4.2 Finalize (finalizeStock)

Behavior:

  1. opens transaction
  2. row-locks inventory row
  3. requires row to exist
  4. ensures reserved >= quantity and total >= quantity
  5. updates:
    • reserved = reserved - quantity
    • total = total - quantity

Error behavior:

  • INVENTORY_NOT_FOUND
  • INVENTORY_INVALID_OPERATION

4.3 Release (releaseStock)

Behavior:

  1. opens transaction
  2. row-locks inventory row
  3. missing row => safe no-op success (idempotent skip)
  4. release quantity = min(requested, reserved)
  5. if release quantity is <= 0, treat as safe no-op success
  6. updates:
    • reserved = reserved - releaseQuantity
    • available = available + releaseQuantity

Error behavior:

  • non-business failures return success: false
  • business no-op branches are logged as [skip]

4.4 Adjust (adjustStock)

Behavior:

  1. opens transaction
  2. row-locks inventory row
  3. if row missing: creates row with normalized total/available values
  4. if row exists: applies delta to total+available
  5. rejects when any resulting quantity becomes negative

Error behavior:

  • INVENTORY_INVALID_ADJUSTMENT

4.5 Sync (syncStock)

Behavior:

  • if productId provided: reconcile only one row
  • otherwise: reconcile all rows where available != total - reserved
  • updates only inconsistent rows

4.6 Low stock (getLowStock)

Predicate:

  • track_inventory = true
  • available_quantity <= threshold

Default threshold:

  • 5 when not provided

5. Processor Runtime (BullMQ)

Processors in apps/api/src/modules/inventory/processors/inventory.processor.ts:

Processor classQueueJob names
InventoryReserveProcessororders_reservationsstock.reserve
InventoryFinalizeProcessororders_stock_finalizestock.finalize
InventoryReleaseProcessororders_stock_releasestock.release
InventoryAdjustProcessorinventorystock.adjust, stock.sync

Processor behavior:

  • strict job-name routing via switch (job.name)
  • structured logs: [start], [success], [failure]
  • delegates business logic to InventoryService
  • unknown jobs are skipped with warning log

6. API Layer and Guards

Admin HTTP surface:

  • /api/admin/inventory

Guards and authorization:

  • JwtAuthGuard
  • RoleGuard
  • permission checks with @Permissions(...)

Permissions used:

  • Inventory_READ
  • Inventory_UPDATE

DTO layer:

  • InventoryListQueryDto
  • StockAdjustDto
  • BulkSyncDto
  • InventoryResponseDto

7. Order Integration Contracts

Inventory lifecycle is driven by order outbox events:

Order event pointInventory eventQueue
order create (checkout/buy-now)stock.reserveorders_reservations
payment successstock.finalizeorders_stock_finalize
cancel/payment_failed/expiredstock.releaseorders_stock_release

Dispatcher ownership in current runtime includes:

  • orders
  • orders_maintenance
  • cart
  • orders_reservations
  • orders_stock
  • orders_stock_finalize
  • orders_stock_release
  • inventory

Known mismatch:

  • orders_stock is owned/registered but has no active processor consumer in current code.

8. Idempotency, Concurrency, and Integrity

Concurrency safeguards:

  • all stock writes are transaction-scoped
  • all lifecycle writes lock target row using FOR UPDATE

Integrity safeguards:

  • service-level guards prevent negative calculations
  • DB check constraints prevent negative persisted quantities

Idempotency status:

  • dedupe keys are generated at outbox emission points
  • queue/job id dedupe is handled in dispatcher (jobId)
  • service-level idempotency ledger table is not implemented (replay safety depends on order/outbox + current state checks)

9. Logging, Monitoring, and Audit

Current runtime logging:

  • inventory service emits structured logs with order/product/quantity context
  • processors emit lifecycle logs by job type

Current audit gap:

  • Mongo AuditLog writes for inventory mutations are not yet implemented in inventory service

Related operational checks:

  • outbox dispatcher + maintenance scheduler monitor queue health and failed thresholds

10. Cache and Config Status

Current cache behavior:

  • inventory service/admin service currently perform direct DB reads/writes
  • inventory cache TTL env knobs exist but are not wired in runtime logic

Configured but currently unused inventory env keys:

  • INVENTORY_STOCK_CACHE_TTL_SECONDS
  • INVENTORY_LIST_CACHE_TTL_SECONDS
  • INVENTORY_CONFIG_CACHE_TTL_SECONDS
  • INVENTORY_HISTORY_CACHE_TTL_SECONDS
  • INVENTORY_RESERVATIONS_CACHE_TTL_SECONDS

11. Error and Resilience Contracts

Inventory-specific error codes:

  • INVENTORY_NOT_FOUND
  • INVENTORY_INSUFFICIENT_STOCK
  • INVENTORY_ALREADY_FINALIZED
  • INVENTORY_ALREADY_RELEASED
  • INVENTORY_INVALID_ADJUSTMENT
  • INVENTORY_INVALID_OPERATION

Current usage note:

  • INVENTORY_ALREADY_FINALIZED and INVENTORY_ALREADY_RELEASED exist but are not emitted by service logic at this time.

Retry model:

  • job retries are BullMQ-configurable via global/queue defaults
  • processors are designed to be retry-safe where possible through state-aware mutation logic

12. Backend Diagrams

12.1 Stock lifecycle ownership

12.2 Admin mutation path

13. Release/QA Checklist

  • Row-locking is present on reserve/finalize/release/adjust paths.
  • Reserve rejects insufficient stock when oversell is disabled.
  • Finalize decreases both reserved and total safely.
  • Release no-op behavior is stable for missing/no-reserved cases.
  • Admin endpoints return ResponseDto shape and correct permission gating.
  • Inventory processors consume all expected inventory jobs.
  • orders_stock queue ownership is either implemented with a consumer or removed.
  • Mongo inventory audit logging gap is closed before production hardening.
  • Inventory env knobs are either wired into runtime or removed from env contract.

14. File Map

  • apps/api/src/modules/inventory/inventory.module.ts
  • apps/api/src/modules/inventory/shared/inventory-shared.module.ts
  • apps/api/src/modules/inventory/shared/inventory.service.ts
  • apps/api/src/modules/inventory/admin/inventory-admin.module.ts
  • apps/api/src/modules/inventory/admin/inventory-admin.controller.ts
  • apps/api/src/modules/inventory/admin/inventory-admin.service.ts
  • apps/api/src/modules/inventory/admin/dto/*
  • apps/api/src/modules/inventory/processors/inventory-processor.module.ts
  • apps/api/src/modules/inventory/processors/inventory.processor.ts
  • packages/db/src/schema/inventory/product-inventory.ts
  • packages/jobs/src/index.ts

15. Environment Variables

VariableDefaultCurrent status
INVENTORY_STOCK_CACHE_TTL_SECONDSunsetdefined in validation, not wired in service
INVENTORY_LIST_CACHE_TTL_SECONDSunsetdefined in validation, not wired in service
INVENTORY_CONFIG_CACHE_TTL_SECONDSunsetdefined in validation, not wired in service
INVENTORY_HISTORY_CACHE_TTL_SECONDSunsetdefined in validation, not wired in service
INVENTORY_RESERVATIONS_CACHE_TTL_SECONDSunsetdefined in validation, not wired in service

See Also