Anik Sikder
Blueprints/nexus-scm-supply-chain-management-platform

Nexus SCM (Supply Chain & Warehouse Management Platform)

Enterprise-grade supply chain management platform designed to unify procurement, warehouse operations, inventory control, distribution, supplier collaboration, and logistics workflows through a ledger-first, event-driven, multi-tenant architecture.

Supply ChainWarehouse ManagementLogisticsInventoryProcurementCQRSEvent DrivenEvent SourcingMulti-TenantSaaSPostgreSQLKafkaDjangoArchitecture
34 min readFebruary 1, 2026
  • Read Time
    34 min read
  • Topics
    14
  • Patterns
    8
  • Level
    Advanced
Architecture Highlights

Ledger-first inventory architecture

Warehouse-scoped stock ownership

Transactional outbox for reliable event publishing

Event-driven warehouse workflows

CQRS read models for operational analytics

Idempotent distributed event processing

Multi-echelon inventory visibility

Automated replenishment and forecasting

blueprint.md

$ open blueprint

Ask most warehouse managers:

"How much of SKU-4471 do we actually have?"

The honest answer is often a pause.

Then:

"Let me check three systems and call you back."

The ERP says 2,400 units.

The warehouse management system says 2,317.

A cycle count from yesterday says 2,281.

Sales has already reserved another 340 units.

And 500 more units are sitting on a truck between Warehouse A and Warehouse B.

None of those numbers are necessarily wrong.

They are simply answering different questions.

The problem is architectural.

Inventory is frequently modeled as a mutable number instead of a traceable history of physical events.

Nexus SCM takes the opposite approach.

Inventory is modeled as a ledger of movements.

Receiving creates inventory.

Picking consumes inventory.

Transfers move inventory between locations.

Cycle counts create explicit adjustments.

Reservations create commitments.

Shipments change ownership and availability.

Every important state transition produces a durable domain event.

The result is a system where:

  • "How much physically exists?"
  • "Where is it?"
  • "How much is available?"
  • "How much is reserved?"
  • "How much is in transit?"
  • "Why did the quantity change?"
  • "What did we have last Tuesday?"

are all answerable from the same underlying operational truth.


Executive Summary

Nexus SCM is designed as a ledger-first, event-driven, multi-tenant supply chain platform.

The architecture separates operational writes from analytical reads while maintaining PostgreSQL as the transactional source of truth for inventory and business state.

The core architectural principle is:

Never make the current inventory number the source of truth. Make the history of inventory movements the source of truth.

Current inventory becomes a projection of that history.

The platform is organized around bounded business domains:

DomainPrimary Responsibility
Identity & AccessAuthentication, authorization, tenant isolation, warehouse permissions
OrganizationTenant configuration, branches, warehouses, operational policies
Product CatalogSKU, product, unit of measure, barcode and packaging definitions
ProcurementRequisitions, RFQs, supplier quotations, purchase orders
ReceivingASN, receiving sessions, inspections, discrepancy handling
Warehouse OperationsPutaway, picking, packing, cycle counting, bin operations
Inventory LedgerImmutable stock movements and inventory state transitions
ReservationStock commitments against sales and fulfillment demand
TransferWarehouse-to-warehouse and location-to-location movement
DistributionShipment planning, allocation, carrier integration
Supplier CollaborationSupplier confirmations, ASN, supplier performance
ForecastingDemand velocity, safety stock, lead-time analysis
ReplenishmentReorder points, purchase requisitions, replenishment policies
ReportingCQRS projections and analytical read models
AuditImmutable business and security audit trail

The architecture intentionally separates business correctness from eventual read optimization.

PostgreSQL owns transactional correctness.

Kafka distributes committed events.

Read models serve high-volume queries.

Celery handles asynchronous jobs.

Redis accelerates frequently accessed projections and operational lookups.


Business Problem

Consider a distributor operating:

  • 8 warehouses
  • 35,000 SKUs
  • 150 suppliers
  • 4,000 inbound receipts per month
  • 50,000 outbound order lines per day
  • thousands of barcode scans per hour
  • multiple transportation providers

A naïve inventory system might store:

code
sku.total_stock = 2317

That number looks useful.

It is actually insufficient.

It cannot answer:

  • Why is stock 2,317?
  • Who changed it?
  • Which warehouse owns it?
  • How much is reserved?
  • How much is damaged?
  • How much is available for sale?
  • How much is currently in transit?
  • What was the quantity yesterday?
  • Which receiving operation created the stock?
  • Which picks consumed it?
  • Was the quantity adjusted after a cycle count?

Nexus models these questions explicitly.


Architectural Principles

The platform follows several non-negotiable architectural principles.

1. Ledger First

Inventory is derived from immutable stock movements.

2. Tenant First

Every business record belongs to an organization.

Cross-tenant access is never inferred from user identity alone.

3. Location Scoped

Inventory operations are scoped to warehouses, zones, bins, and physical locations.

4. Transactional Truth Before Events

The database transaction commits the business truth first.

Events are published only after the transaction commits.

5. Idempotent Consumers

Every external and internal event consumer must safely process duplicates.

6. Explicit State Machines

Important workflows are represented as valid state transitions instead of arbitrary status updates.

7. CQRS for Read Scalability

Operational writes and analytical reads have different models.

8. No Distributed Transaction Across Services

The system uses transactional boundaries, outbox events, idempotency, and eventual consistency rather than trying to maintain a global distributed transaction.

9. Auditability by Default

Every consequential inventory and procurement action has a traceable actor, timestamp, reference, and reason.

10. Physical Reality Is the Source of Business Meaning

The system should model what physically happened, not merely what someone clicked in the UI.


High-Level Architecture

code
                           ┌──────────────────────────┐
                           │     Client Applications  │
                           │                          │
                           │ Web · Scanner · Mobile   │
                           │ Supplier Portal · APIs   │
                           └────────────┬─────────────┘
                                        │
                                        ▼
                           ┌──────────────────────────┐
                           │       API Gateway        │
                           │                          │
                           │ Auth · Rate Limit · TLS  │
                           │ Routing · Tenant Context │
                           └────────────┬─────────────┘
                                        │
              ┌─────────────────────────┼─────────────────────────┐
              │                         │                         │
              ▼                         ▼                         ▼
       ┌─────────────┐          ┌──────────────┐          ┌──────────────┐
       │ Procurement │          │   Warehouse  │          │ Distribution │
       │   Domain    │          │    Domain    │          │   Domain     │
       └──────┬──────┘          └──────┬───────┘          └──────┬───────┘
              │                        │                         │
              └────────────────────────┼─────────────────────────┘
                                       │
                                       ▼
                         ┌──────────────────────────┐
                         │   Inventory Domain       │
                         │                          │
                         │ Ledger · Reservation     │
                         │ Transfer · Availability  │
                         └────────────┬─────────────┘
                                      │
                                      ▼
                         ┌──────────────────────────┐
                         │      PostgreSQL          │
                         │                          │
                         │ Transactional State      │
                         │ Inventory Ledger         │
                         │ Outbox                   │
                         └────────────┬─────────────┘
                                      │
                                      │ committed events
                                      ▼
                         ┌──────────────────────────┐
                         │     Transactional        │
                         │        Outbox            │
                         └────────────┬─────────────┘
                                      │
                                      ▼
                         ┌──────────────────────────┐
                         │          Kafka           │
                         │                          │
                         │ Domain Event Backbone    │
                         └────────────┬─────────────┘
                                      │
              ┌───────────────────────┼────────────────────────┐
              │                       │                        │
              ▼                       ▼                        ▼
       ┌─────────────┐        ┌─────────────┐         ┌────────────────┐
       │ Read Model  │        │ Forecasting │         │ Notifications  │
       │ Consumers   │        │ & Reorder   │         │ & Integrations │
       └──────┬──────┘        └─────────────┘         └────────────────┘
              │
              ▼
       ┌────────────────┐
       │ Query Stores   │
       │ / Projections  │
       └────────────────┘

                         ┌───────────────────────┐
                         │       Celery          │
                         │                       │
                         │ Forecasting           │
                         │ Reconciliation        │
                         │ Scheduled Jobs        │
                         └───────────┬───────────┘
                                     │
                                     ▼
                                   Redis

Why PostgreSQL Remains the Transactional Core

Kafka is not the source of truth for inventory.

This distinction matters.

A warehouse receipt must guarantee that:

code
Receipt recorded
        +
Inventory movement recorded
        +
PO line updated
        +
Outbox event recorded

either all happen or none happen.

The transaction should therefore look conceptually like:

code
with transaction.atomic():

    receipt = create_receipt(...)

    movement = StockMovement.objects.create(
        warehouse=warehouse,
        sku=sku,
        quantity=received_quantity,
        movement_type="receipt",
        reference_id=receipt.id,
    )

    purchase_order_line.mark_received(received_quantity)

    OutboxEvent.objects.create(
        aggregate_type="stock_movement",
        aggregate_id=movement.id,
        event_type="inventory.stock_received",
        payload=event_payload,
    )

Only after PostgreSQL commits should the event become available to Kafka.

This prevents a dangerous situation where:

code
Database transaction succeeds
        │
        ├── Inventory updated
        │
        └── Kafka publish fails

without any durable record of the event.

The Outbox Pattern closes that gap.


Transactional Outbox Pattern

The architecture uses PostgreSQL as the transactional boundary and an outbox table as the bridge between database state and Kafka.

code
Warehouse Scan
     │
     ▼
Application Service
     │
     ▼
PostgreSQL Transaction
     │
     ├── StockMovement
     ├── Receipt
     ├── Business State
     └── OutboxEvent
             │
             ▼
        COMMIT
             │
             ▼
      Outbox Publisher
             │
             ▼
           Kafka

If Kafka is temporarily unavailable:

code
PostgreSQL
    │
    └── OutboxEvent remains pending
              │
              ▼
       Retry Publisher
              │
              ▼
            Kafka

No inventory transaction needs to wait for Kafka availability.

This is a critical distinction between an architectural diagram that merely contains Kafka and an architecture that can actually survive production failures.


Inventory Domain

The Inventory Domain is the operational center of Nexus SCM.

Its responsibility is not simply:

code
"How many units do we have?"

It answers:

code
What happened to every unit,
where is it now,
what state is it in,
and who or what caused the change?

The domain contains several concepts:

code
SKU
 │
 ├── Inventory Balance
 │
 ├── Stock Movement
 │
 ├── Reservation
 │
 ├── Inventory State
 │
 ├── Location
 │
 └── Availability

Inventory Ledger

A simplified ledger model:

code
class StockMovement:
    organization_id
    warehouse_id
    location_id
    sku_id

    movement_type
    quantity

    reference_type
    reference_id

    occurred_at
    recorded_at

    actor_id

    idempotency_key
    metadata

Examples:

code
+500 receipt
-20 pick
-5 damage
+5 cycle-count-adjustment
-50 transfer-out
+50 transfer-in

The ledger is append-only.

A correction does not modify the previous record.

Instead:

code
Incorrect Movement
        │
        ▼
Compensating Movement

For example:

code
Original:
+100 received

Correction:
-10 receiving_adjustment

The original event remains visible.

This gives the platform forensic capability.


Inventory Equation

For a given SKU and warehouse:

code
On Hand
=
Σ all confirmed inventory movements

But operational availability is more nuanced:

code
Available
=
On Hand
- Reserved
- Allocated
- Blocked

And network availability may include:

code
Available to Promise
=
Available Now
+ Eligible Incoming Stock
- Existing Commitments

This distinction prevents the common mistake of treating:

code
physical stock

and:

code
sellable stock

as the same number.


Inventory States

Nexus should explicitly model inventory state.

A useful model is:

code
AVAILABLE
RESERVED
ALLOCATED
PICKED
PACKED
DAMAGED
QUARANTINED
BLOCKED
IN_TRANSIT

The important point is that these states should not simply be arbitrary strings.

They represent business invariants.

For example:

code
QUARANTINED stock
    ↓
cannot become AVAILABLE
    ↓
until an authorized disposition occurs

Similarly:

code
RESERVED
    ↓
ALLOCATED
    ↓
PICKED
    ↓
PACKED
    ↓
SHIPPED

The system should reject invalid transitions.


Warehouse Hierarchy

Inventory needs more granularity than warehouse-level ownership.

A realistic hierarchy is:

code
Organization
    │
    └── Warehouse
          │
          ├── Zone
          │     │
          │     ├── Aisle
          │     │     │
          │     │     └── Bin
          │     │
          │     └── Aisle
          │
          ├── Receiving Area
          ├── Picking Area
          ├── Packing Area
          ├── Quarantine Area
          └── Dispatch Area

Therefore:

code
Stock
=
Organization
+
Warehouse
+
Location
+
SKU
+
Inventory State

This allows queries such as:

code
How much SKU-4471 is available
in Warehouse A
inside Zone C
excluding quarantine stock?

without introducing duplicate inventory records.


Inventory Reservation

Reservations should not mutate physical inventory.

A reservation represents a commitment.

code
Physical Inventory
        │
        ├── 1,000 On Hand
        │
        └── 300 Reserved
                 │
                 ▼
          700 Available

This distinction is essential.

A sales order may reserve inventory before warehouse staff physically pick it.

Therefore:

code
Reservation ≠ Stock Movement

The two concepts are related but not identical.


Reservation Lifecycle

code
Demand Created
      │
      ▼
Reservation Requested
      │
      ▼
Availability Checked
      │
      ▼
Reservation Created
      │
      ├───────────────┐
      ▼               ▼
Allocated          Released
      │
      ▼
Picked
      │
      ▼
Consumed

If an order is cancelled:

code
Reserved
   │
   ▼
Released

No fake inventory movement is necessary simply because a reservation disappeared.


Warehouse Transfers

Warehouse transfers are modeled as a stateful workflow.

code
TRANSFER_REQUESTED
        │
        ▼
ALLOCATED
        │
        ▼
DISPATCHED
        │
        ▼
IN_TRANSIT
        │
        ▼
RECEIVED
        │
        ▼
COMPLETED

The physical inventory equation must remain correct at every stage.

Example:

code
Warehouse A
    -50

In Transit
    +50

Warehouse B
     0

Total network inventory:

code
50

No inventory has been created or destroyed.

When Warehouse B receives:

code
Warehouse A
    -50

In Transit
     0

Warehouse B
    +50

Total:

code
50

Transfer Idempotency

A receiving scanner may retry the same request because the handheld device temporarily loses connectivity.

Without idempotency:

code
Receive 50
Retry
Receive 50 again

would produce:

code
+100

instead of:

code
+50

Therefore every physical operation should carry an idempotency key:

code
warehouse_id
+
device_id
+
operation_id

Example:

code
scanner-17:transfer-8842-receive

The database enforces uniqueness.


Warehouse Receiving

Receiving begins before inventory becomes available.

code
Advanced Shipping Notice
        │
        ▼
Inbound Shipment
        │
        ▼
Receiving Session
        │
        ▼
Barcode Scan
        │
        ▼
Quantity Validation
        │
        ▼
Quality Inspection
        │
        ├── Accepted
        │
        └── Quarantined
        │
        ▼
Inventory Movement
        │
        ▼
Putaway Task

This avoids the dangerous assumption:

code
"Scanned" = "Available"

A damaged or quarantined item should not automatically become sellable stock.


Putaway Workflow

code
Received
   │
   ▼
Putaway Task Created
   │
   ▼
Optimal Bin Selected
   │
   ▼
Picker Scans Item
   │
   ▼
Picker Scans Destination Bin
   │
   ▼
Location Validation
   │
   ▼
Putaway Confirmed

The system should validate:

code
SKU matches
+
warehouse matches
+
destination is valid
+
quantity is valid
+
task is still active

before committing the movement.


Picking Architecture

Picking is modeled as a workflow rather than a simple stock decrement.

code
Order
 │
 ▼
Allocation
 │
 ▼
Pick Wave
 │
 ▼
Pick Task
 │
 ▼
Scanner
 │
 ▼
SKU Verification
 │
 ▼
Bin Verification
 │
 ▼
Quantity Confirmation
 │
 ▼
Stock Movement
 │
 ▼
Packing

The scanner should never directly manipulate a stock quantity.

It sends a command.

The application validates the command.

The inventory domain creates the movement.


Command vs Event

This distinction is important.

A scanner sends a command:

code
ConfirmPick

The system produces an event:

code
inventory.stock_picked

Commands express intent.

Events express facts.

code
Command:
"Pick 5 units."

Event:
"5 units were picked."

This distinction keeps domain boundaries clean.


Event-Driven Architecture

The event bus is not responsible for determining whether an operation is valid.

The domain transaction determines validity.

Kafka distributes the resulting fact.

Example:

code
ConfirmReceiving Command
          │
          ▼
Inventory Transaction
          │
          ├── StockMovement
          ├── Receipt
          └── OutboxEvent
                    │
                    ▼
                  Kafka
                    │
       ┌────────────┼───────────────┐
       ▼            ▼               ▼
 Procurement   Forecasting     Audit Projection

The receiving service does not directly call forecasting.

It publishes a fact.

Forecasting decides whether it cares.


Domain Event Structure

Events should contain stable metadata.

Example:

code
{
  "event_id": "evt_01JX...",
  "event_type": "inventory.stock_received",
  "event_version": 1,
  "occurred_at": "2026-08-18T10:30:00Z",

  "organization_id": "org_123",
  "warehouse_id": "wh_001",

  "aggregate_type": "stock_movement",
  "aggregate_id": "mov_88321",

  "correlation_id": "rcv_8821",
  "causation_id": "cmd_9912",

  "payload": {
    "sku_id": "SKU-4471",
    "quantity": 500,
    "location_id": "BIN-A-17"
  }
}

Important metadata includes:

  • event_id
  • event_type
  • event_version
  • organization_id
  • aggregate_id
  • correlation_id
  • causation_id
  • occurred_at

This makes distributed debugging significantly easier.


Kafka Topic Strategy

Events should be organized by business domain rather than creating one giant topic.

Example:

code
inventory.events
warehouse.events
procurement.events
transfer.events
shipment.events
supplier.events

Partitioning should preserve ordering where ordering matters.

For inventory events, a useful partition key may be:

code
organization_id + warehouse_id + sku_id

This helps preserve event ordering for a specific inventory stream while allowing unrelated SKUs to process in parallel.


Event Ordering

Distributed systems cannot assume global event ordering.

Consider:

code
Pick Confirmed
Shipment Confirmed
Carrier Event

These may arrive at consumers in an unexpected order.

Consumers should therefore use:

code
event timestamp
+
sequence/version
+
state transition validation

rather than assuming:

code
event A always arrives before event B

Idempotent Event Consumption

Kafka provides at-least-once delivery semantics in many practical architectures.

Therefore consumers must assume:

code
same event
      ↓
may arrive twice

A consumer can maintain:

code
ProcessedEvent
----------------
event_id
consumer_name
processed_at

Before processing:

code
if already_processed(event.event_id):
    return

The important part is that event processing and recording the processed state should itself be atomic where possible.


Failure Handling

A production architecture must assume failure.

Examples:

code
Kafka unavailable
Database unavailable
Scanner retries
Supplier sends duplicate ASN
Carrier sends duplicate webhook
Consumer crashes after processing
Consumer crashes before acknowledging
Network timeout after successful transaction

Nexus handles these through:

code
Transactions
+
Outbox
+
Idempotency
+
Retries
+
Dead Letter Queues
+
Reconciliation Jobs

Dead Letter Queue

Events that repeatedly fail should not block an entire consumer.

code
Kafka
 │
 ▼
Consumer
 │
 ├── Success ───────────────▶ Processed
 │
 └── Failure
       │
       ▼
    Retry
       │
       ├── Success
       │
       └── Repeated Failure
                │
                ▼
              DLQ

The DLQ should preserve enough information to investigate and replay the event safely.


Reconciliation

Even a strongly designed distributed system needs reconciliation.

Examples:

code
Inventory Projection
        vs
Ledger
code
PO Received Quantity
        vs
Inventory Receipts
code
Shipment State
        vs
Carrier State

Scheduled reconciliation jobs can detect divergence.

code
Ledger
   │
   ▼
Recalculate Projection
   │
   ▼
Compare
   │
   ├── Match
   │
   └── Divergence
          │
          ▼
      Alert / Repair

This is particularly important for long-running enterprise systems.


CQRS Architecture

The operational database should not become the analytical warehouse.

The write model is optimized for:

code
transactions
consistency
constraints
append operations
state transitions

The read model is optimized for:

code
aggregation
filtering
dashboards
historical reporting
warehouse comparison
forecasting

Architecture:

code
                     ┌─────────────────────┐
                     │ PostgreSQL Write DB │
                     │                     │
                     │ Source of Truth     │
                     └──────────┬──────────┘
                                │
                                ▼
                           Domain Events
                                │
                                ▼
                              Kafka
                                │
             ┌──────────────────┼─────────────────┐
             ▼                  ▼                 ▼
      Inventory Read      Procurement Read   Analytics Read
         Model               Model              Model
             │                  │                 │
             └──────────────────┴─────────────────┘
                                │
                                ▼
                         Reporting APIs

Read Model Design

Instead of executing:

code
SELECT
    warehouse,
    sku,
    SUM(quantity)
FROM stock_movements
GROUP BY warehouse, sku;

for every dashboard request, Nexus can maintain:

code
inventory_balance_projection

containing:

code
organization_id
warehouse_id
location_id
sku_id

on_hand
reserved
allocated
available
in_transit

last_movement_at

This allows the operational UI to answer:

code
"How much SKU-4471 is available?"

without scanning millions of historical movements.


Rebuildable Projections

Read models are disposable.

If the projection logic changes:

code
Ledger
   │
   ▼
Replay Events
   │
   ▼
New Projection

The system does not need to modify historical truth.

This gives the architecture a powerful property:

The read model can evolve independently from the source of truth.


Procurement Architecture

Procurement is modeled as a state machine.

code
REQUISITION
     │
     ▼
RFQ
     │
     ▼
QUOTATION
     │
     ▼
APPROVAL
     │
     ▼
PURCHASE ORDER
     │
     ▼
SUPPLIER CONFIRMED
     │
     ▼
SHIPPED
     │
     ▼
RECEIVED
     │
     ▼
CLOSED

Invalid transitions should be rejected.

For example:

code
CLOSED → SUPPLIER_CONFIRMED

should not be possible without an explicit reversal workflow.


Three-Way Matching

Procurement and finance should converge around:

code
Purchase Order
       +
Goods Receipt
       +
Supplier Invoice

The system compares:

code
Ordered Quantity
Received Quantity
Invoiced Quantity

and:

code
Ordered Price
Received Price
Invoiced Price

A mismatch becomes an exception rather than silently progressing toward payment.


Supplier Integration

External suppliers are inherently unreliable.

They may:

  • send duplicate confirmations
  • resend ASNs
  • change delivery dates
  • send partial shipments
  • send incorrect quantities
  • fail to respond

Therefore supplier integrations should be designed as asynchronous boundaries.

code
Supplier
   │
   ▼
Integration Gateway
   │
   ▼
Validation
   │
   ▼
Idempotency
   │
   ▼
Domain Command
   │
   ▼
Procurement Domain

External payloads should never directly mutate internal database state.


Distribution Architecture

Outbound fulfillment follows:

code
Customer Demand
       │
       ▼
Inventory Availability
       │
       ▼
Reservation
       │
       ▼
Allocation
       │
       ▼
Pick Wave
       │
       ▼
Picking
       │
       ▼
Packing
       │
       ▼
Shipment
       │
       ▼
Carrier
       │
       ▼
Delivery

Each stage produces an explicit business fact.


Carrier Integration

Carrier events arrive asynchronously.

Example:

code
shipment.created
shipment.dispatched
shipment.in_transit
shipment.out_for_delivery
shipment.delivered
shipment.exception

Carrier event IDs should be used for deduplication.

The system should not assume carrier events arrive exactly once or in order.


Multi-Tenant Architecture

The tenant boundary is fundamental.

code
Organization
     │
     ├── Users
     ├── Warehouses
     ├── Suppliers
     ├── Products
     ├── Orders
     ├── Inventory
     └── Procurement

Every tenant-owned record should carry:

code
organization_id

where appropriate.

Application services should establish tenant context before executing domain queries.

Conceptually:

code
queryset.filter(
    organization_id=current_organization.id
)

But tenant isolation should not rely solely on developers remembering filters.

Defense in depth may include:

code
Application-level tenant isolation
+
Database constraints
+
PostgreSQL Row-Level Security where appropriate
+
Authorization policies
+
Automated tests

Warehouse-Scoped Authorization

Tenant membership alone is insufficient.

A user may belong to an organization but only have access to:

code
Warehouse A

The authorization model can therefore represent:

code
User
 │
 └── Organization Membership
        │
        ├── Role
        │
        └── Warehouse Scope

Example:

code
Warehouse Picker
    → Warehouse A
    → Read inventory
    → Execute picks
    → Cannot adjust inventory

Warehouse Manager
    → Warehouse A
    → Warehouse B
    → Adjust inventory
    → Approve cycle counts

Procurement Manager
    → All warehouses
    → Suppliers
    → Purchase Orders

Permissions should follow least privilege.


Product and SKU Architecture

A product is not necessarily the same thing as a stock-keeping unit.

A realistic model separates:

code
Product
   │
   └── SKU
        │
        ├── Barcode
        ├── Unit of Measure
        ├── Packaging
        ├── Supplier SKU
        └── Warehouse Configuration

This allows:

code
1 carton = 24 units

without corrupting the underlying inventory model.


Units of Measure

Supply chains frequently operate across units.

Examples:

code
Piece
Box
Carton
Pallet
Kilogram
Liter
Meter

Conversions should be explicit.

code
1 carton
=
24 pieces

The system should define which unit represents the inventory base quantity.

For example:

code
Base Unit = piece

while purchasing may occur in:

code
carton

This avoids ambiguous quantities.


Cycle Counting

Cycle counts should not overwrite inventory.

Instead:

code
Expected Quantity
        │
        ▼
Physical Count
        │
        ▼
Variance
        │
        ▼
Approval
        │
        ▼
Adjustment Movement

Example:

code
Expected = 200
Physical = 178

Variance = -22

The system creates:

code
cycle_count_adjustment = -22

The original expected balance remains reconstructable.


Forecasting Architecture

Forecasting should consume movement history rather than manually maintained spreadsheets.

code
Stock Movements
      │
      ▼
Consumption Projection
      │
      ▼
Demand Velocity
      │
      ▼
Seasonality / Trend
      │
      ▼
Forecast
      │
      ▼
Replenishment Engine

Forecasting can consider:

code
Historical demand
Lead time
Safety stock
Seasonality
Supplier reliability
Minimum order quantity
Order frequency
Current inventory
Incoming inventory
Existing reservations

Reorder Point

A simplified model:

code
Reorder Point
=
Demand During Lead Time
+
Safety Stock

If:

code
Current Available
<
Reorder Point

the system can generate a replenishment recommendation.

But auto-purchasing should be governed by policy.

code
Forecast
   │
   ▼
Reorder Recommendation
   │
   ▼
Policy Engine
   │
   ├── Auto Approve
   │
   ├── Manager Approval
   │
   └── Manual Review

This is safer than blindly generating purchase orders.


Celery Architecture

Celery should handle work that does not belong in the synchronous request path.

Examples:

code
Forecast generation
Reorder calculations
Supplier synchronization
Carrier synchronization
Report generation
Projection repair
Reconciliation
Notification delivery
Data exports

The API should not block waiting for these operations.


Redis Usage

Redis should be treated as an acceleration layer, not the source of inventory truth.

Good uses:

code
Hot inventory projections
Rate limiting
Distributed locks where justified
Short-lived workflow state
Task coordination
Cache

Bad use:

code
Redis = inventory source of truth

If Redis disappears, the system should remain correct.

It may become slower.

It must not become wrong.


Concurrency Control

Inventory operations are inherently concurrent.

Two scanners might attempt:

code
Pick 10 units

at almost exactly the same time.

If only 12 units are available:

code
Request A → 10
Request B → 10

the system must not end with:

code
-8 available

The inventory transaction must enforce the invariant.

Possible strategies include:

code
Row-level locking
Optimistic concurrency
Serializable transactions where appropriate
Reservation constraints
Atomic conditional updates

The choice should be based on the exact contention characteristics of the inventory model.


Inventory Invariants

Production systems should make business invariants explicit.

Examples:

code
A stock movement must belong to one organization.

A warehouse must belong to the same organization as its stock movement.

A transfer-out cannot exceed transferable stock.

A quarantined quantity cannot be allocated.

A completed shipment cannot return to PACKED without an explicit reversal.

A closed purchase order cannot receive additional goods without reopening or exception handling.

A reservation cannot exceed allocatable inventory.

A duplicate physical operation must not create duplicate stock.

These invariants belong in domain services and database constraints where practical.


Database Architecture

Primary transactional store:

code
PostgreSQL

Logical separation may look like:

code
Core Transactional Tables
        │
        ├── Organization
        ├── Warehouse
        ├── SKU
        ├── Inventory
        ├── StockMovement
        ├── Reservation
        ├── Transfer
        ├── PurchaseOrder
        └── Shipment

Infrastructure Tables
        │
        ├── OutboxEvent
        ├── IdempotencyKey
        ├── ProcessedEvent
        └── AuditLog

Indexes should reflect operational access patterns rather than being added indiscriminately.


Inventory Indexing

Typical access patterns include:

code
organization + warehouse + sku
organization + sku + movement_time
warehouse + location + sku
reference_type + reference_id
idempotency_key

For very large ledgers, time-based partitioning may eventually become appropriate:

code
stock_movements_2026_01
stock_movements_2026_02
stock_movements_2026_03
...

Partitioning should be introduced based on measured data volume and query behavior, not simply because the architecture diagram contains millions of rows.


Scaling Strategy

Nexus should scale in layers.

Stage 1

code
Django
+
PostgreSQL
+
Redis
+
Celery

Stage 2

code
Read replicas
+
CQRS projections
+
Background workers

Stage 3

code
Kafka
+
Event-driven integrations
+
Independent consumers

Stage 4

code
Partitioned ledgers
+
Dedicated reporting infrastructure
+
Horizontal worker scaling

Stage 5

code
Domain extraction
+
Independent service deployment

The architecture should not start with dozens of microservices merely because the system is "enterprise."


Modular Monolith First

A realistic implementation can begin as a modular Django application:

code
nexus/
├── identity/
├── organizations/
├── catalog/
├── procurement/
├── receiving/
├── warehouse/
├── inventory/
├── reservations/
├── transfers/
├── distribution/
├── suppliers/
├── forecasting/
├── replenishment/
├── reporting/
├── integrations/
└── audit/

Each module owns its domain logic.

Communication should happen through:

code
Application Services
+
Domain Events
+
Explicit Contracts

rather than arbitrary cross-module database manipulation.

This allows the architecture to evolve toward independently deployed services later without prematurely paying the operational cost of microservices.


Service Extraction Strategy

If scale eventually requires service extraction:

code
Modular Monolith
       │
       ▼
Identify High-Load Boundary
       │
       ▼
Define Contract
       │
       ▼
Introduce Events
       │
       ▼
Extract Consumer
       │
       ▼
Separate Deployment

Likely candidates for extraction could eventually include:

code
Forecasting
Carrier Integration
Supplier Integration
Reporting
Notification

The inventory ledger should be extracted only when there is a compelling operational reason.

It is the most consistency-sensitive domain.


API Architecture

The API should expose business operations rather than arbitrary CRUD mutations.

Prefer:

code
POST /receipts/{id}/confirm
POST /pick-tasks/{id}/confirm
POST /transfers/{id}/dispatch
POST /transfers/{id}/receive
POST /reservations
POST /cycle-counts/{id}/approve

over:

code
PATCH /inventory/123
{
    "quantity": 178
}

The former expresses business intent.

The latter bypasses the domain model.


API Idempotency

Critical commands should support:

code
Idempotency-Key

Especially:

code
Receive
Pick
Ship
Transfer
Reserve
Release
Purchase Order Confirmation

A retry should return the result of the original operation instead of creating another operation.


Observability Architecture

Production supply chain systems need more than logs.

Nexus should provide:

code
Metrics
+
Structured Logs
+
Distributed Tracing
+
Audit Events
+
Business Monitoring

Important metrics include:

code
Stock movement throughput
Kafka consumer lag
Outbox backlog
Failed event count
DLQ depth
Pick completion rate
Receiving throughput
Inventory adjustment rate
Reservation failure rate
Supplier confirmation latency
Carrier event latency
Forecast execution time

Correlation and Traceability

A single warehouse operation should be traceable across the system.

Example:

code
Scanner Request
      │
      │ correlation_id
      ▼
API
      │
      ▼
Inventory Transaction
      │
      ▼
Outbox
      │
      ▼
Kafka
      │
      ├── Forecasting
      ├── Reporting
      └── Audit

If a warehouse manager asks:

"Why did SKU-4471 change by 22 units?"

the system should be able to trace:

code
Movement
   ↓
Command
   ↓
User
   ↓
Device
   ↓
Warehouse
   ↓
Reference Document
   ↓
Related Events

Audit Architecture

Audit logging is separate from inventory history.

Inventory history answers:

code
What happened to stock?

Security audit answers:

code
Who performed what administrative action?

Examples:

code
User permission changed
Warehouse access granted
Supplier bank information modified
Inventory adjustment approved
Purchase order approval changed

Audit records should be immutable and access-controlled.


Security Architecture

Security boundaries exist at several levels:

code
Internet
   │
   ▼
TLS
   │
   ▼
API Gateway
   │
   ▼
Authentication
   │
   ▼
Tenant Resolution
   │
   ▼
Authorization
   │
   ▼
Warehouse Scope
   │
   ▼
Domain Operation

Security should not depend on frontend restrictions.

The backend must enforce:

code
Tenant
+
Role
+
Warehouse Scope
+
Resource Ownership
+
Operation Permission

Data Consistency Model

Not every piece of information needs strong consistency.

Strong Consistency

Use transactional consistency for:

code
Inventory movement
Reservation creation
Transfer dispatch
Receiving confirmation
Purchase order state transition

Eventual Consistency

Use eventual consistency for:

code
Dashboards
Forecasts
Notifications
Search indexes
Analytics
Supplier performance projections

This distinction is fundamental.

Trying to make everything strongly consistent increases system complexity and reduces scalability without providing meaningful business value.


Source of Truth Matrix

DataSource of Truth
Inventory movementsPostgreSQL Ledger
ReservationsPostgreSQL Transactional Model
Purchase OrdersProcurement Domain
Warehouse TasksWarehouse Domain
Shipment StateDistribution Domain
Event DeliveryKafka
Read ModelsDerived Projections
CacheRedis
ForecastForecasting Projection
AuditImmutable Audit Store

Kafka is therefore an event backbone, not the canonical inventory database.

Redis is a cache, not the canonical inventory database.

Read models are projections, not canonical inventory state.


Failure Scenarios

Database Commits, Kafka Fails

code
Database
   │
   ├── Inventory committed
   └── Outbox committed
              │
              ▼
         Kafka unavailable

Result:

code
Inventory remains correct.
Outbox retries later.

Kafka Publishes, Consumer Crashes

code
Kafka
  │
  ▼
Consumer
  │
  ├── Business processing
  │
  └── Crash

The event may be delivered again.

Idempotency ensures:

code
one business effect

even if:

code
multiple delivery attempts

occur.


Scanner Times Out

The scanner may not know whether the operation succeeded.

It retries using:

code
same idempotency key

The server responds with the original result.


Carrier Sends Duplicate Webhook

code
carrier_event_id = 77881

is already processed.

The second delivery becomes a no-op.


Disaster Recovery

The transactional database is the critical recovery asset.

The architecture should provide:

code
Automated PostgreSQL backups
+
Point-in-time recovery
+
Replica strategy
+
Kafka retention
+
Projection rebuild capability

A key architectural property is:

Read models can be recreated. Operational truth cannot be casually recreated after being destroyed.

Therefore backup and recovery priorities should focus first on transactional truth.


Disaster Recovery Flow

code
Primary PostgreSQL Failure
          │
          ▼
Recovery / Replica
          │
          ▼
Restore Transactional State
          │
          ▼
Resume Outbox Publishing
          │
          ▼
Kafka Consumers Resume
          │
          ▼
Rebuild Missing Projections

Security and Operational Boundaries

The architecture should distinguish:

code
Business Data
Operational Events
Security Audit
Integration Credentials
Secrets
Logs

Sensitive credentials should never appear in:

code
Kafka payloads
application logs
audit metadata
error responses

Secrets should be managed through a dedicated secrets-management mechanism rather than committed to application configuration.


Architecture Evolution

The platform should evolve incrementally.

code
Phase 1
Modular Django + PostgreSQL
        │
        ▼
Phase 2
Ledger + Reservations + Warehouse Workflows
        │
        ▼
Phase 3
Transactional Outbox
        │
        ▼
Phase 4
Kafka + Event Consumers
        │
        ▼
Phase 5
CQRS Read Models
        │
        ▼
Phase 6
Forecasting + Replenishment
        │
        ▼
Phase 7
Advanced Integrations
        │
        ▼
Phase 8
Selective Service Extraction

The architecture becomes more distributed only when business scale justifies it.


End-to-End Receiving Example

A complete receiving operation looks like:

code
Supplier Shipment Arrives
        │
        ▼
ASN Matched
        │
        ▼
Receiving Session Created
        │
        ▼
Scanner Reads Barcode
        │
        ▼
Command Sent to API
        │
        ▼
Tenant + Warehouse Authorization
        │
        ▼
Idempotency Check
        │
        ▼
Database Transaction
        │
        ├── Receipt Line Updated
        ├── Stock Movement Created
        ├── Inventory State Updated
        ├── PO Quantity Updated
        └── Outbox Event Created
        │
        ▼
COMMIT
        │
        ▼
Scanner Success
        │
        ▼
Outbox Publisher
        │
        ▼
Kafka
        │
        ├── Forecasting
        ├── Reporting
        ├── Procurement
        └── Audit

The scanner does not wait for all downstream systems.

Only the critical transaction needs to complete before confirming the operation.


End-to-End Picking Example

code
Sales Order
     │
     ▼
Availability Check
     │
     ▼
Reservation
     │
     ▼
Allocation
     │
     ▼
Pick Task
     │
     ▼
Scanner
     │
     ▼
Confirm Pick
     │
     ▼
Inventory Transaction
     │
     ├── Stock Movement
     ├── Reservation Consumption
     └── Outbox Event
     │
     ▼
Kafka
     │
     ├── Read Model
     ├── Analytics
     └── Notification

The inventory mutation remains transactional.

The downstream consequences remain asynchronous.


End-to-End Transfer Example

code
Transfer Requested
        │
        ▼
Availability Check
        │
        ▼
Stock Allocated
        │
        ▼
Dispatch
        │
        ├── Source Inventory Reduced
        ├── In-Transit Position Created
        └── Event Published
        │
        ▼
Carrier / Internal Transport
        │
        ▼
Destination Receiving
        │
        ▼
Idempotency Check
        │
        ▼
Transfer-In Transaction
        │
        ├── In-Transit Reduced
        ├── Destination Increased
        └── Event Published
        │
        ▼
Transfer Completed

The system never needs to pretend that stock teleported from one warehouse to another.


Real-World Architectural Parallels

Flexport

The architecture reflects the same broad principle found in modern logistics platforms: shipment visibility is based on continuously changing operational events rather than a single static shipment status.

ShipBob

Modern fulfillment platforms demonstrate the importance of connecting inventory, warehouse execution, order fulfillment, and distributed inventory visibility.

Manhattan Associates

Large-scale warehouse management demonstrates why barcode-driven execution, location-aware inventory, allocation, picking, and operational correctness must be treated as one integrated system.

The goal is not to reproduce these companies.

The goal is to apply the architectural principles that make systems operating at that class of complexity possible.


What I Would Build Today

For an initial production implementation:

code
Application
    Django
    Django REST Framework

Database
    PostgreSQL

Caching
    Redis

Background Processing
    Celery

Event Backbone
    Kafka

Event Reliability
    Transactional Outbox

Architecture
    Modular Monolith
    + Domain Events
    + CQRS Read Models

Infrastructure
    Docker
    Managed PostgreSQL
    Managed Kafka where practical

Observability
    Structured Logging
    Metrics
    Distributed Tracing

I would not start by deploying:

code
15 microservices
+
multiple databases
+
Kafka
+
Kubernetes

before the business boundaries have proven that they need them.

Architecture is not about maximizing infrastructure.

Architecture is about preserving correctness while creating room for scale.


What I Would Explicitly Avoid

Mutable Inventory Counters as Source of Truth

code
inventory.quantity = 178

Avoid.

Instead:

code
StockMovement(
    quantity=-22,
    movement_type="cycle_count_adjustment"
)

Direct Database Mutation From Every Module

Avoid:

code
WarehouseService()
    → directly edits Procurement tables

Prefer:

code
Command
   ↓
Owning Domain
   ↓
Domain Event
   ↓
Interested Consumers

Kafka as the Inventory Database

Avoid:

code
Kafka = source of truth

Use:

code
PostgreSQL = transactional truth
Kafka = event distribution

Redis as Inventory Truth

Avoid:

code
Redis quantity = authoritative quantity

Redis should be disposable.


Synchronous Chains Across Domains

Avoid:

code
Receive
 ↓
Procurement API
 ↓
Forecasting API
 ↓
Notification API
 ↓
Reporting API
 ↓
Success

One downstream failure should not cause a warehouse scanner to fail.

Prefer:

code
Receive
 ↓
Transactional Commit
 ↓
Outbox
 ↓
Kafka
 ↓
Independent Consumers

Global Distributed Transactions

Avoid trying to make:

code
PostgreSQL
+
Kafka
+
Supplier API
+
Carrier API

one giant transaction.

Distributed systems fail.

The architecture should tolerate that fact.


Architecture Decision Records

Important architectural decisions should be documented explicitly.

Examples:

code
ADR-001
Inventory is modeled as an append-only ledger.

ADR-002
PostgreSQL remains the transactional source of truth.

ADR-003
Kafka is used for asynchronous event distribution.

ADR-004
Transactional Outbox guarantees reliable event publication.

ADR-005
Read models are derived through CQRS projections.

ADR-006
Warehouse transfers explicitly model IN_TRANSIT state.

ADR-007
Physical operations require idempotency keys.

ADR-008
The initial implementation uses a modular monolith.

ADR-009
Redis is an acceleration layer, never canonical state.

ADR-010
External integrations are asynchronous and idempotent.

These decisions are more valuable architecturally than simply listing technologies.


Architecture Quality Attributes

Nexus is optimized around several quality attributes.

AttributeArchitectural Mechanism
CorrectnessTransactional ledger + invariants
AuditabilityImmutable movements + audit events
ScalabilityCQRS + asynchronous consumers
ReliabilityOutbox + retries + idempotency
AvailabilityAsync downstream processing
PerformanceRead projections + Redis
SecurityTenant + warehouse-scoped authorization
RecoverabilityPostgreSQL backup + projection rebuild
MaintainabilityDomain boundaries + modular architecture
ExtensibilityDomain events + integration contracts
ObservabilityCorrelation IDs + metrics + tracing

The Architectural Core

Everything eventually comes back to one decision:

code
                    Physical Reality
                          │
                          ▼
                  Business Command
                          │
                          ▼
                 Domain Validation
                          │
                          ▼
                Transactional Ledger
                          │
                          ▼
                     Commit
                          │
                          ▼
                  Domain Event
                          │
                          ▼
                       Kafka
                          │
          ┌───────────────┼────────────────┐
          ▼               ▼                ▼
      Read Models     Forecasting      Integrations
          │               │                │
          ▼               ▼                ▼
     Dashboards       Reordering       Suppliers

The database transaction establishes what actually happened.

The event stream communicates that fact.

The read models make the fact easy to query.

The automation reacts to the fact.

The integrations propagate the fact.

That separation is what makes the architecture resilient.


Evolution Path

code
Single Warehouse
        │
        ▼
Ledger-Based Inventory
        │
        ▼
Reservations & Availability
        │
        ▼
Multi-Warehouse Inventory
        │
        ▼
Explicit In-Transit Modeling
        │
        ▼
Warehouse Execution
        │
        ▼
Procurement Integration
        │
        ▼
Transactional Outbox
        │
        ▼
Kafka Event Backbone
        │
        ▼
CQRS Read Models
        │
        ▼
Carrier & Supplier Integrations
        │
        ▼
Forecasting
        │
        ▼
Automated Replenishment
        │
        ▼
Selective Domain Extraction

The critical insight is that the architecture does not need to become distributed on day one.

The domain boundaries and invariants need to be correct on day one.

Infrastructure can evolve as scale demands it.


What Makes Nexus SCM an Architecture

Nexus is not simply:

code
Django
+
PostgreSQL
+
Kafka
+
Redis
+
Celery

That is a technology stack.

The architecture is the set of decisions connecting those technologies to business requirements:

code
Inventory Accuracy
        ↓
Ledger

Operational Reliability
        ↓
Transactional Outbox

High-Frequency Warehouse Writes
        ↓
Transactional Write Model

Complex Reporting
        ↓
CQRS Read Models

Distributed Processing
        ↓
Kafka

External Failure
        ↓
Idempotency + Retry + DLQ

Multi-Warehouse Movement
        ↓
Explicit Transfer + IN_TRANSIT State

Tenant Isolation
        ↓
Organization + Warehouse Scope

Forecasting
        ↓
Movement History + Async Processing

Long-Term Scalability
        ↓
Modular Boundaries + Selective Extraction

That is architecture.


Key Takeaways

Nexus SCM treats the supply chain as a distributed physical system rather than a collection of CRUD tables.

The architecture is built around several foundational decisions:

  • Inventory is a ledger, not a mutable number.
  • Physical operations are transactional commands that produce durable business facts.
  • PostgreSQL is the transactional source of truth.
  • Transactional Outbox guarantees that committed business events are eventually published.
  • Kafka distributes events but does not own inventory truth.
  • Consumers are idempotent because duplicate delivery is expected.
  • Warehouse transfers explicitly model inventory in transit.
  • Reservations are commitments, not physical stock movements.
  • CQRS separates operational writes from analytical reads.
  • Read models are disposable and rebuildable projections.
  • Redis accelerates the system but never owns canonical state.
  • Celery handles asynchronous and scheduled workloads.
  • Multi-tenancy and warehouse scope are enforced as architectural boundaries.
  • External suppliers and carriers are treated as unreliable distributed-system participants.
  • Reconciliation is part of the architecture, not an emergency procedure.
  • A modular monolith provides a pragmatic starting point before selective service extraction.
  • Observability, auditability, idempotency, and failure recovery are first-class architectural concerns.

The fundamental philosophy is simple:

The system should never need to guess what happened to inventory.

Every meaningful movement has a cause.

Every cause has a transaction.

Every transaction can produce an event.

Every event can be traced.

Every projection can be rebuilt.

And every warehouse, supplier, order, shipment, and forecast ultimately derives from a system that can explain where the number came from.

That is the architecture behind Nexus SCM.

status: blueprint_loaded

$ ls related_articles

status: end_of_file

Related Blueprints

Continue exploring related architecture patterns.

Nexus SCM (Supply Chain & Warehouse Management Platform)

Enterprise-grade supply chain management platform designed to unify procurement, warehouse operations, inventory control, distribution, supplier collaboration, and logistics workflows through a ledger-first, event-driven, multi-tenant architecture.

01Ledger-first inventory architecture
02Warehouse-scoped stock ownership
03Transactional outbox for reliable event publishing
04Event-driven warehouse workflows
05CQRS read models for operational analytics
06Idempotent distributed event processing
07Multi-echelon inventory visibility
08Automated replenishment and forecasting

FinCore Treasury (Financial Operations & Treasury Platform)

Architecture blueprint for a multi-tenant treasury platform covering bank connectivity, cash management, payment orchestration, reconciliation, approval governance, immutable financial accounting, and event-driven financial operations.

01Multi-bank cash visibility
02Event-driven payment orchestration
03Automated reconciliation engine
04Tiered approval governance
05Immutable financial ledger

TalentFlow HCM (Human Capital Management Platform)

Enterprise-grade human capital management platform designed to unify employee lifecycle management, payroll operations, recruitment workflows, attendance tracking, performance management, and workforce analytics within a secure multi-tenant architecture.

01WebSocket-first real-time communication
02Versioned contracts and acceptance workflows
03Double-entry escrow and wallet ledger
04Atomic milestone state transitions
05Transactional outbox and idempotent events
06Dispute-driven fund freezing
07CQRS read models for messaging and transactions