Anik Sikder
Blueprints/biznex-os-unified-business-operating-system

BizNex OS (Unified Business Operating System)

Enterprise-grade business operating system designed to unify inventory, accounting, sales, procurement, billing, and organizational workflows within a secure multi-tenant SaaS architecture.

ERPBusiness Operating SystemSaaSMulti-TenantDjangoDDDRBACAccountingInventory Management
29 min readNovember 8, 2025
  • Read Time
    29 min read
  • Topics
    9
  • Patterns
    5
  • Level
    Advanced
Architecture Highlights

Double-entry accounting core

Ledger-based inventory engine

Domain-bounded ERP modules

Tenant-isolated financial records

Event-ready modular monolith

blueprint.md

$ open blueprint

The high-level architecture explains the major domains inside BizNex OS.

The deeper architectural question is:

How do those domains maintain correctness when a single business operation affects inventory, accounting, sales, procurement, billing, and reporting at the same time?

That is the central problem an ERP architecture must solve.

A customer placing an order is not simply a database insert.

It may cause:

code
Sales Order
    │
    ├── Inventory Reservation
    │
    ├── Pricing Calculation
    │
    ├── Tax Calculation
    │
    ├── Invoice Creation
    │
    ├── Payment Recording
    │
    ├── Stock Movement
    │
    ├── Accounting Journal
    │
    └── Audit Event

The architecture must ensure that these operations happen according to clearly defined invariants.

The goal is not merely:

"Everything succeeded."

The goal is:

The business state remains mathematically, financially, and operationally correct even when requests fail, workers crash, users retry operations, or external systems become unavailable.


Architecture Principles

BizNex OS is built around several architectural invariants.

1. Financial State Is Derived From Immutable Transactions

The system never treats a mutable balance as the ultimate source of truth.

code
Transaction
     │
     ▼
Journal Entry
     │
     ▼
Ledger
     │
     ▼
Account Balance
     │
     ▼
Financial Report

The balance is a projection of history.

Not the other way around.


2. Inventory Is a Movement System

Inventory is represented by movements.

code
Purchase Receipt      +100
Sales Shipment         -30
Customer Return         +5
Adjustment               -2
Transfer Out            -10
Transfer In             +10
──────────────────────────────
Available Stock          73

The current quantity is derived from movements and reservation state.

This makes historical reconstruction possible.


3. Every Domain Owns Its Invariants

Accounting owns:

code
Debits = Credits

Inventory owns:

code
Stock movements are valid
Warehouse quantities cannot violate rules

Sales owns:

code
Order lifecycle
Pricing
Invoice lifecycle

Procurement owns:

code
Purchase lifecycle
Approval rules
Supplier commitments

No domain should depend on another domain's internal implementation details.


4. Cross-Domain Operations Are Orchestrated

A sale may involve:

code
Sales
Inventory
Accounting
Payments
Audit

But the sales domain should not directly manipulate the internal tables of those domains.

Instead:

code
Sales Application Service
        │
        ├── Inventory Service
        ├── Accounting Service
        ├── Payment Service
        └── Audit Service

The orchestration layer coordinates the workflow.


5. Tenant Context Is Mandatory

Every tenant-owned operation carries:

code
organization_id

Tenant context is established before domain operations begin.

code
Request
   │
   ▼
Identity
   │
   ▼
Organization Context
   │
   ▼
Domain Operation

The system never assumes that a client-provided organization ID proves authorization.


Complete System Architecture

Putting the major pieces together:

code
                         CLIENT APPLICATIONS
                    Web / POS / Mobile / API
                               │
                               ▼
                         API Gateway
                               │
                               ▼
                     Authentication / RBAC
                               │
                               ▼
                       Tenant Context
                               │
                               ▼
                     Application Services
                               │
        ┌──────────────────────┼────────────────────────┐
        │                      │                        │
        ▼                      ▼                        ▼
   Sales Domain          Inventory Domain        Procurement Domain
        │                      │                        │
        │                      │                        │
        └──────────────┬───────┴───────────────┬────────┘
                       │                       │
                       ▼                       ▼
                Accounting Domain       Billing / Payments
                       │                       │
                       └───────────┬───────────┘
                                   ▼
                         Domain Event / Outbox
                                   │
                       ┌───────────┼────────────┐
                       ▼           ▼            ▼
                    Celery      Notifications  Reporting
                       │
                       ▼
                  External Systems

                       DATA LAYER
                            │
          ┌─────────────────┼──────────────────┐
          ▼                 ▼                  ▼
      PostgreSQL          Redis           Object Storage
      System of Record    Cache           Documents/Exports

The most important architectural boundary is between:

code
Domain State

and:

code
Derived / Asynchronous State

PostgreSQL contains authoritative business state.

Redis, reports, search indexes, dashboards, and notifications are derived or operational projections.


Domain Architecture

BizNex should be organized around business capabilities rather than database tables.

code
biznex/
│
├── identity/
│
├── organization/
│
├── catalog/
│
├── sales/
│
├── inventory/
│
├── procurement/
│
├── accounting/
│
├── billing/
│
├── payments/
│
├── reporting/
│
├── audit/
│
└── shared/

Each domain should contain its own:

code
Models
Services
Repositories
Policies
Validators
Domain Events
Selectors

The purpose is not to create arbitrary folder boundaries.

The purpose is to make ownership explicit.


Domain Ownership Matrix

DomainOwnsDoes Not Own
IdentityUsers, memberships, authenticationFinancial state
OrganizationTenant configuration, branchesTransactions
CatalogProducts, SKUs, pricing definitionsStock quantities
SalesOrders, order lines, sales lifecycleLedger balances
InventoryMovements, reservations, warehousesFinancial journals
ProcurementPurchase requests, POs, supplier lifecycleCustomer invoices
AccountingAccounts, journals, ledgersPhysical stock
BillingInvoices, payment stateWarehouse movements
ReportingRead models and analytical projectionsAuthoritative transactional state
AuditSecurity/business event historyOperational business state

This table is more important than the folder structure.

It defines who is allowed to change what.


Aggregate Boundaries

Domain-driven design becomes especially important around aggregates.

For example:

code
SalesOrder
   │
   ├── Customer
   ├── OrderLines
   ├── Pricing
   └── OrderStatus

The SalesOrder aggregate controls its own invariants.

For example:

code
Order cannot be confirmed
if it contains no valid line items.

Inventory may have:

code
InventoryItem
   │
   ├── SKU
   ├── Warehouse
   ├── Quantity
   └── Reservation State

Accounting may have:

code
Journal
   │
   ├── Journal Lines
   ├── Fiscal Period
   └── Posting State

The key principle:

An aggregate protects its invariants; it does not become a dumping ground for unrelated business logic.


Accounting Architecture

Accounting is the financial backbone of the platform.

The fundamental structure is:

code
Business Transaction
       │
       ▼
Accounting Event
       │
       ▼
Journal Entry
       │
       ▼
Journal Lines
       │
       ▼
General Ledger
       │
       ▼
Financial Statements

Chart of Accounts

Each organization owns its chart of accounts.

code
1000 Assets
    │
    ├── 1100 Cash
    ├── 1200 Accounts Receivable
    └── 1300 Inventory

2000 Liabilities
    │
    ├── 2100 Accounts Payable
    └── 2200 Tax Payable

3000 Equity

4000 Revenue

5000 Cost of Goods Sold

6000 Operating Expenses

The chart defines classification.

It does not contain the transaction history.

Transaction history belongs to journals and ledger entries.


Double-Entry Invariant

Every posted journal must satisfy:

code
SUM(Debits) = SUM(Credits)

For example:

code
Inventory Purchase

Debit
Inventory Asset       $10,000

Credit
Accounts Payable      $10,000

The journal is balanced.

For a sale:

code
Revenue Recognition

Debit
Accounts Receivable    $15,000

Credit
Sales Revenue          $15,000

And separately:

code
Cost Recognition

Debit
Cost of Goods Sold      $9,000

Credit
Inventory Asset         $9,000

A single business operation can therefore produce multiple accounting events.


Journal Lifecycle

A journal should have a controlled lifecycle.

code
Draft
  │
  ▼
Validated
  │
  ▼
Posted
  │
  ▼
Locked

Once posted:

code
UPDATE journal
SET amount = ...

should not be the correction mechanism.

Instead:

code
Original Journal
      │
      ▼
Reversing Journal
      │
      ▼
Correct Journal

This preserves history.


Fiscal Period Control

Accounting periods should be explicit.

code
2026-01
2026-02
2026-03
...

A posted transaction should not silently modify a closed period.

code
Closed Period
     │
     ▼
New Correction
     │
     ▼
Adjustment Entry

This is critical for reliable financial reporting.


Inventory Architecture

Inventory is not simply:

code
Product.stock_quantity

It is a system of state transitions.

code
Purchase
   │
   ▼
Receipt
   │
   ▼
Stock Movement
   │
   ▼
Available Inventory

Sales:

code
Sales Order
   │
   ▼
Reservation
   │
   ▼
Shipment
   │
   ▼
Stock Movement

Returns:

code
Customer Return
   │
   ▼
Inspection
   │
   ├── Sellable ─────► Stock Increase
   │
   └── Damaged ──────► Separate Inventory State

Inventory State Model

A useful inventory model separates multiple concepts.

code
On Hand
    │
    ├── Available
    ├── Reserved
    ├── Damaged
    ├── Quarantined
    └── In Transit

Therefore:

code
Available
≠
On Hand

For example:

code
On Hand       = 100
Reserved      = 30
Available     = 70

This is much more useful than one mutable stock_quantity field.


Inventory Movement Model

Every movement records:

code
movement_id
organization_id
product_id
warehouse_id
location_id
quantity
movement_type
reference_type
reference_id
occurred_at
created_by

Example:

code
Movement
--------------------------------
SKU          = SKU-1029
Warehouse    = WH-01
Quantity     = -30
Type         = SALE_SHIPMENT
Reference    = SO-4021

This creates a complete stock history.


Inventory Reservations

Reservation is different from physical stock movement.

code
Order Created
     │
     ▼
Reserve 30
     │
     ▼
Available decreases
     │
     ▼
Shipment
     │
     ▼
On-Hand decreases

Therefore:

code
Reservation
≠
Stock Movement

This distinction prevents many ERP inventory bugs.


Concurrency in Inventory

Inventory is highly vulnerable to race conditions.

Imagine:

code
Available Stock = 1

Two customers purchase simultaneously.

code
Request A ──► Check stock = 1
Request B ──► Check stock = 1
Request A ──► Reserve
Request B ──► Reserve

Now the system has oversold.

The architecture must therefore make reservation atomic.

Conceptually:

code
BEGIN

Lock inventory state

Check available quantity

Create reservation

Commit

END

In PostgreSQL this can be implemented using appropriate row-level locking and transactional constraints.

The principle is:

The check and the state change must happen inside the same consistency boundary.


Sales Architecture

Sales should have an explicit lifecycle.

code
Draft
  │
  ▼
Confirmed
  │
  ▼
Reserved
  │
  ▼
Fulfilled
  │
  ▼
Invoiced
  │
  ▼
Paid
  │
  ▼
Completed

Not every business requires every state, but transitions should be explicit.

A random:

code
order.status = "paid"

should never bypass required domain rules.


Order Aggregate

A simplified model:

code
SalesOrder
    │
    ├── Customer
    │
    ├── Order Lines
    │      ├── Product
    │      ├── Quantity
    │      ├── Unit Price
    │      └── Tax
    │
    ├── Pricing Snapshot
    ├── Tax Snapshot
    └── Status

Historical orders should preserve the commercial facts that were agreed at the time.

If the product price changes tomorrow, yesterday's invoice should not change.


Pricing Architecture

Catalog pricing and transaction pricing are different concepts.

code
Product Catalog Price
        │
        ▼
Pricing Engine
        │
        ├── Customer Segment
        ├── Discount
        ├── Promotion
        ├── Quantity
        └── Tax Rules
        │
        ▼
Transaction Price

Once an order is confirmed, the relevant pricing information should be captured as a transaction snapshot.

This prevents historical transactions from changing when catalog configuration changes.


Procurement Architecture

Procurement follows its own lifecycle.

code
Purchase Request
       │
       ▼
Approval
       │
       ▼
Purchase Order
       │
       ▼
Goods Receipt
       │
       ▼
Supplier Invoice
       │
       ▼
Three-Way Match
       │
       ▼
Payment

Each state transition is explicit.


Three-Way Matching

The procurement system compares:

code
Purchase Order
      │
      ├── Quantity
      ├── Price
      └── Supplier
      │
      ▼
Goods Receipt
      │
      ├── Quantity Received
      └── Date
      │
      ▼
Supplier Invoice
      │
      ├── Quantity
      ├── Price
      └── Supplier

Only when the configured tolerance rules are satisfied should the invoice become payable.

For example:

code
PO Quantity       = 100
Received Quantity = 100
Invoice Quantity  = 100

PO Price           = $10
Invoice Price      = $10

Match succeeds.

If:

code
Invoice Quantity = 130

the system should require exception handling rather than silently approving payment.


Cross-Domain Transaction Architecture

This is the most important part of BizNex OS.

Consider:

Customer purchases 10 units for $1,000.

The business event touches:

code
Sales
Inventory
Accounting
Billing
Audit

The architecture should define the transaction boundary carefully.


Modular Monolith Transaction

Because BizNex begins as a modular monolith, tightly coupled operations can share one PostgreSQL transaction.

code
BEGIN TRANSACTION

Create Sales Order
       │
       ▼
Reserve Inventory
       │
       ▼
Create Invoice
       │
       ▼
Create Accounting Journal
       │
       ▼
Create Audit Event
       │
       ▼
Create Outbox Events

COMMIT

If a critical operation fails:

code
ROLLBACK

The database returns to the previous consistent state.

This is one of the strongest reasons to start with a modular monolith.


Transaction Boundary

However, not every operation belongs inside one transaction.

Good candidates for the primary transaction:

code
Order State
Inventory Reservation
Invoice State
Journal Entry
Outbox Record

Poor candidates:

code
Send Email
Generate PDF
Call External Payment Gateway
Notify Slack
Send Webhook
Generate Large Report

Those should happen asynchronously.

Therefore:

code
Core State Change
       │
       ▼
Database Transaction
       │
       ├── Business State
       └── Outbox Event
              │
              ▼
        Asynchronous Workers

Transactional Outbox

The outbox solves the dual-write problem.

Bad:

code
Save Order
   │
   ▼
Publish Event
   │
   X
Broker unavailable

The order exists, but downstream systems never receive the event.

Better:

code
BEGIN

Save Order

Save Outbox Event

COMMIT

Then:

code
Outbox Worker
      │
      ▼
Publish Event
      │
      ▼
Mark Event Published

The database becomes the source of truth for the fact that the event needs to be published.


Domain Events

A sale can generate:

code
SalesOrderConfirmed
InventoryReserved
InvoiceCreated
JournalPosted
PaymentReceived

Events should describe facts.

Good:

code
InvoiceCreated

Avoid events that encode commands:

code
CreateInvoice

The distinction matters.

A command says:

"Please do this."

An event says:

"This already happened."


Event Flow

code
Sales Domain
     │
     ▼
SalesOrderConfirmed
     │
     ├────────► Inventory
     │
     ├────────► Billing
     │
     ├────────► Accounting
     │
     └────────► Reporting

Each consumer decides what that event means for its own domain.


Idempotency

Enterprise systems receive retries.

A payment provider may retry a webhook.

A worker may process the same event twice.

A client may submit the same request twice.

Therefore, important operations must be idempotent.

Example:

code
Payment Webhook
    │
    ▼
event_id = evt_82921
    │
    ▼
Already Processed?
    │
 ┌──┴───┐
Yes    No
 │      │
Skip   Process

Without idempotency:

code
Webhook Retry
     │
     ▼
Payment Recorded Twice
     │
     ▼
Financial Reconciliation Failure

Idempotency is therefore not an optimization.

It is part of correctness.


Distributed Evolution

Eventually, a domain may need to become an independent service.

The architecture can evolve:

code
                    TODAY

              Modular Monolith
                     │
       ┌─────────────┼─────────────┐
       ▼             ▼             ▼
     Sales       Inventory     Accounting


                    LATER

              Distributed Domains

       Sales Service ──────► Event Bus
                                  │
                   ┌──────────────┼──────────────┐
                   ▼              ▼              ▼
              Inventory       Accounting      Billing

But once a domain becomes a separate service:

code
Shared Database
      X

should generally become:

code
Service
   │
   ▼
Own Database

and cross-domain consistency must move from ACID database transactions toward asynchronous workflows and compensating actions.


Saga Architecture

Consider an order workflow after services become distributed.

code
Create Order
    │
    ▼
Reserve Inventory
    │
    ▼
Authorize Payment
    │
    ▼
Create Invoice
    │
    ▼
Complete Order

If payment fails:

code
Payment Failed
      │
      ▼
Release Inventory
      │
      ▼
Cancel Order

The system compensates for previously completed actions.

This is fundamentally different from a modular monolith transaction.

code
Modular Monolith
     │
     ▼
ACID Transaction

versus:

code
Distributed System
     │
     ▼
Saga + Events + Compensation

That distinction should be part of the long-term BizNex architecture.


Reporting Architecture

Reporting should not put heavy analytical queries directly against critical transactional tables whenever possible.

code
Transactional Database
          │
          ▼
      Domain Events
          │
          ▼
   Reporting Projection
          │
          ▼
      Dashboards

For example:

code
Sales Orders
     │
     ▼
Sales Projection
     │
     ├── Daily Revenue
     ├── Product Margin
     ├── Customer Sales
     └── Sales Trends

The reporting model can be optimized for reads without compromising the transactional model.


Financial Reporting

Financial reports should ultimately derive from accounting data.

Examples:

code
Trial Balance
     │
     ▼
General Ledger

Balance Sheet
     │
     ▼
Account Classification

Income Statement
     │
     ▼
Revenue - Expenses

Cash Flow
     │
     ▼
Cash-related Transactions

The reporting layer should never independently calculate financial truth from random sales tables.

The accounting ledger remains authoritative.


Inventory Reporting

Inventory reporting can use projections:

code
Stock Movement Ledger
       │
       ▼
Inventory Projection
       │
       ├── Current Stock
       ├── Available Stock
       ├── Reserved Stock
       ├── Stock Valuation
       └── Movement History

The projection can be rebuilt.

That is important.

If:

code
InventoryProjection

becomes corrupted, the system should be able to reconstruct it from authoritative movements.


Reconciliation Architecture

An ERP should continuously verify its own invariants.

Examples:

code
Accounting:
Total Debits = Total Credits
code
Inventory:
Opening + Movements = Closing
code
Procurement:
PO / Receipt / Invoice differences within tolerance
code
Payments:
Gateway totals = Internal payment totals

These checks should run automatically.

code
Scheduled Reconciliation
          │
          ▼
Invariant Checks
          │
      ┌───┴────┐
      ▼        ▼
   Healthy   Mismatch
               │
               ▼
             Alert

Reconciliation should not be a month-end activity only.

It should be a continuous control.


Audit Architecture

BizNex needs both operational history and security/business audit history.

code
Business Action
      │
      ├────────► Domain History
      │
      └────────► Audit Event

Example:

code
{
  "event": "purchase_order.approved",
  "organization_id": "org_44",
  "actor_id": "usr_1029",
  "purchase_order_id": "po_8821",
  "amount": "12500.00",
  "timestamp": "2026-08-18T09:14:02Z"
}

For financial records, the accounting journal itself provides the authoritative financial history.

The audit layer provides the operational context:

code
Who approved it?
When?
From which session?
Under which organization?
Which workflow caused it?

Tenant Isolation Architecture

Tenant isolation must exist across every layer.

code
                    Tenant Isolation
                          │
       ┌──────────────────┼───────────────────┐
       ▼                  ▼                   ▼
     API Layer        Database Layer      Cache Layer
       │                  │                   │
       ▼                  ▼                   ▼
 organization_id     organization_id     tenant-aware key

But it must also extend to:

code
Queues
Reports
Exports
Documents
Search
Object Storage
Audit Logs
Webhooks
Notifications

For example:

code
Celery Job
    │
    ├── organization_id
    ├── actor_id
    └── resource_id

A background worker must never process a tenant resource based solely on an unvalidated object ID.


Branch and Warehouse Context

Multi-tenant ERP systems often require another level:

code
Organization
     │
     ├── Branch A
     │      ├── Warehouse A1
     │      └── Warehouse A2
     │
     ├── Branch B
     │      └── Warehouse B1
     │
     └── Branch C

Authorization can therefore become:

code
Organization Scope
        │
        ▼
Branch Scope
        │
        ▼
Warehouse Scope
        │
        ▼
Resource Scope

A warehouse manager might have:

code
inventory.read
inventory.adjust

but only:

code
warehouse = WH-02

This is where RBAC and resource scoping meet.


Concurrency Architecture

ERP systems are concurrency-heavy.

Multiple users can simultaneously:

code
Sell the same SKU
Approve the same PO
Receive the same shipment
Process the same payment
Edit the same order

The architecture therefore needs explicit concurrency controls.

Possible mechanisms include:

code
Database Transactions
Row-Level Locks
Optimistic Versioning
Unique Constraints
Idempotency Keys
State Transition Guards

For example:

code
Purchase Order
Version = 7

User A reads version 7.

User B reads version 7.

User A updates:

code
7 → 8

User B attempts:

code
7 → 8

The system detects the stale version and rejects the update.

This prevents silent lost updates.


State Machines

Business workflows should be modeled as valid transitions.

For example:

code
DRAFT
  │
  ▼
CONFIRMED
  │
  ▼
FULFILLED
  │
  ▼
INVOICED
  │
  ▼
PAID

Invalid:

code
PAID → DRAFT

unless a specific reversal workflow exists.

State transitions should therefore be domain operations:

code
order.confirm()
order.cancel()
order.fulfill()
order.mark_paid()

rather than unrestricted field mutation:

code
order.status = "paid"

The domain should own the transition rules.


API Idempotency

For financial operations, clients should be able to provide an idempotency key.

code
POST /payments
Idempotency-Key: pay_82921

The server stores:

code
organization_id
idempotency_key
request_hash
response
created_at

If the same request arrives again:

code
Same Key
   │
   ▼
Existing Result
   │
   ▼
Return Original Response

This prevents duplicate financial operations caused by network retries.


External Payment Architecture

External payment systems should not be treated as authoritative for internal financial state without verification.

code
Client
   │
   ▼
BizNex
   │
   ▼
Payment Gateway
   │
   ▼
Webhook
   │
   ▼
Signature Verification
   │
   ▼
Idempotency Check
   │
   ▼
Payment State Update
   │
   ▼
Accounting Journal

The webhook becomes the trusted asynchronous confirmation path after cryptographic verification.


Failure Handling

ERP architecture must explicitly define what happens when dependencies fail.

Redis unavailable

Redis should not become the source of financial truth.

code
Redis Down
    │
    ▼
Database remains authoritative

Performance may degrade.

Financial correctness must not.


Celery unavailable

Core synchronous business transactions should continue whenever the operation does not require an external asynchronous dependency.

Outbox events remain stored:

code
PostgreSQL
    │
    ▼
Outbox
    │
    X
Celery unavailable

When workers recover:

code
Outbox
   │
   ▼
Worker
   │
   ▼
Process Pending Events

External Payment Gateway unavailable

Do not mark the payment as successful merely because the request was sent.

code
Payment Request
      │
      ▼
Gateway Timeout
      │
      ▼
Payment = Pending

Later:

code
Webhook / Reconciliation
      │
      ▼
Confirmed
      │
      ▼
Accounting Entry

Data Integrity Layers

BizNex should enforce correctness at multiple levels.

code
Application Rules
       │
       ▼
Domain Invariants
       │
       ▼
Database Constraints
       │
       ▼
Reconciliation

For example, accounting should not rely only on Python code to ensure balanced journals.

The system can additionally enforce structural constraints around journal lines and posting workflows.

The principle is:

Important business invariants should have more than one line of defense.


Database Constraints

Examples include:

code
Unique SKU per Organization
Unique Account Code per Organization
Unique Membership per User + Organization
Unique Idempotency Key per Organization
Foreign Key Integrity
Non-negative quantities where applicable
Valid status transitions

Database constraints protect against bugs from:

code
API
Admin scripts
Background jobs
Management commands
Future services

They provide a final integrity boundary.


Security Architecture

ERP systems contain high-value data:

code
Financial Records
Supplier Pricing
Customer Data
Employee Data
Inventory
Payments
Business Reports

Therefore security should be layered.

code
Identity
   │
   ▼
Authentication
   │
   ▼
Tenant Isolation
   │
   ▼
RBAC
   │
   ▼
Resource Scope
   │
   ▼
Business Authorization
   │
   ▼
Audit

No single permission check should be responsible for the entire security model.


Authorization Example

Consider:

Warehouse Manager attempts to adjust inventory.

The request becomes:

code
User
 │
 ▼
Authenticated?
 │
 ▼
Organization Membership?
 │
 ▼
Role = Warehouse Manager?
 │
 ▼
Permission = inventory.adjust?
 │
 ▼
Warehouse Scope?
 │
 ▼
SKU belongs to Organization?
 │
 ▼
Adjustment Policy?
 │
 ▼
ALLOW / DENY

If allowed:

code
Inventory Adjustment
       │
       ▼
Stock Movement
       │
       ▼
Accounting Impact
       │
       ▼
Audit Event

One action can therefore affect multiple domains while remaining governed by clear ownership.


Observability Architecture

BizNex needs three forms of observability.

Logs

code
What happened?

Metrics

code
How often?
How quickly?
How many failures?

Traces

code
Where did the workflow spend time?

For a sale:

code
Request
 │
 ├── Sales Service
 │
 ├── Inventory Reservation
 │
 ├── Accounting
 │
 ├── Payment
 │
 └── Audit

Every workflow should carry:

code
request_id
organization_id
actor_id
correlation_id

This makes cross-domain troubleshooting possible.


Business Metrics

Observability should not stop at infrastructure.

ERP systems need business metrics too.

Examples:

code
Orders per minute
Revenue per hour
Inventory turnover
Stockout rate
Purchase approval time
Invoice aging
Payment failure rate
Unreconciled transactions
Journal imbalance attempts

This allows engineering teams to detect business anomalies, not just CPU spikes.


Reconciliation Engine

A mature ERP should have a dedicated reconciliation capability.

code
                    Reconciliation Engine
                            │
          ┌─────────────────┼─────────────────┐
          ▼                 ▼                 ▼
      Accounting         Inventory         Payments
          │                 │                 │
          ▼                 ▼                 ▼
      Trial Balance    Stock Ledger      Gateway Match

Example:

code
Expected Inventory Value
          │
          ▼
Accounting Inventory Asset
          │
          ▼
Compare
          │
      ┌───┴────┐
      ▼        ▼
    Match   Difference
                │
                ▼
             Alert

The system should make discrepancies visible instead of silently correcting them.


Reporting and Read Models

Transactional models are optimized for correctness.

Reporting models are optimized for queries.

Those are different concerns.

code
                 Transactional Model
                         │
                         ▼
                    Domain Events
                         │
                         ▼
                  Reporting Pipeline
                         │
                         ▼
                    Read Models
                         │
              ┌──────────┼──────────┐
              ▼          ▼          ▼
           Revenue     Margin     Inventory

This allows reporting workloads to grow without constantly competing with operational transactions.


Document Architecture

ERP systems generate:

code
Invoices
Purchase Orders
Receipts
Statements
Reports
Exports

Large documents should not be generated synchronously during critical transactions.

Instead:

code
Invoice Created
      │
      ▼
Document Job
      │
      ▼
Celery Worker
      │
      ▼
PDF Generation
      │
      ▼
Object Storage
      │
      ▼
Signed / Authorized URL

The invoice's financial state remains independent of PDF generation.


File and Object Storage Isolation

Documents must carry tenant context.

code
tenant/
   org_44/
      invoices/
      purchase-orders/
      receipts/
      reports/

Access should be authorized through the application rather than assuming knowledge of an object path is sufficient permission.


Scheduled Jobs

ERP systems contain many scheduled operations:

code
Daily reconciliation
Invoice reminders
Payment retries
Stock valuation
Report generation
Subscription billing
Data cleanup

Every scheduled job should be:

code
Tenant-aware
Idempotent
Retryable
Observable
Auditable where appropriate

A job should never assume:

code
"if it runs twice, nothing bad happens."

Instead, the architecture should deliberately make duplicate execution safe.


ERP Control Plane vs Operational Plane

A useful architectural distinction is:

code
                  BizNex OS
                     │
        ┌────────────┴────────────┐
        ▼                         ▼
    Control Plane            Operational Plane
        │                         │
        ├── Identity              ├── Sales
        ├── RBAC                  ├── Inventory
        ├── Policies              ├── Procurement
        ├── Configuration         ├── Billing
        └── Workflows             └── Accounting

The control plane determines:

Who can do what, under which configuration.

The operational plane records:

What the business actually did.

This separation makes the system easier to reason about.


Complete Sale Architecture

Consider a POS transaction:

Customer buys 3 units of SKU-1029 for $300.

The complete architecture becomes:

code
                         POS
                          │
                          ▼
                     API Gateway
                          │
                          ▼
                    Authentication
                          │
                          ▼
                    Tenant Context
                          │
                          ▼
                    Sales Service
                          │
                          ▼
                  Validate Product
                          │
                          ▼
                   Calculate Price
                          │
                          ▼
                 Check Availability
                          │
                          ▼
                 Reserve Inventory
                          │
                          ▼
                  Create Order
                          │
                          ▼
                 Create Invoice
                          │
                          ▼
                 Process Payment
                          │
                          ▼
                Confirm Transaction
                          │
              ┌───────────┴───────────┐
              ▼                       ▼
        Inventory Movement       Accounting Journal
              │                       │
              └───────────┬───────────┘
                          ▼
                     Audit Event
                          │
                          ▼
                    Outbox Event
                          │
                          ▼
                     Background
                      Processing

The entire operation is not merely a "checkout request."

It is a coordinated business transaction.


What Happens When the Sale Fails?

Suppose:

code
Order Created
     │
Inventory Reserved
     │
Payment Fails

The system must define the business compensation:

code
Payment Failed
      │
      ▼
Release Reservation
      │
      ▼
Cancel / Keep Order Pending
      │
      ▼
Audit Event

The exact workflow depends on the business model.

The important point is that failure behavior is explicitly modeled.


Complete Procurement Architecture

code
Purchase Request
       │
       ▼
Authorization Check
       │
       ▼
Approval Workflow
       │
       ▼
Purchase Order
       │
       ▼
Supplier
       │
       ▼
Goods Receipt
       │
       ▼
Inventory Movement
       │
       ▼
Supplier Invoice
       │
       ▼
Three-Way Match
       │
       ▼
Accounts Payable
       │
       ▼
Accounting Journal
       │
       ▼
Payment

This demonstrates why ERP architecture cannot be designed module-by-module without thinking about cross-domain flows.


Complete Accounting Flow

For a $10,000 inventory purchase:

code
Purchase Order
      │
      ▼
Goods Received
      │
      ▼
Inventory +$10,000
      │
      ▼
Accounts Payable +$10,000
      │
      ▼
Journal Posted

Journal:

code
Debit
Inventory Asset       10,000

Credit
Accounts Payable      10,000

Later, when payment occurs:

code
Debit
Accounts Payable      10,000

Credit
Cash                  10,000

The financial state is therefore reconstructable from journal history.


Architecture Invariants

BizNex should define non-negotiable invariants.

code
Invariant 1
Every posted journal is balanced.

Invariant 2
Posted financial history is never overwritten.

Invariant 3
Every inventory state change produces a movement.

Invariant 4
A reservation cannot exceed available inventory.

Invariant 5
Every tenant-owned record belongs to exactly one organization.

Invariant 6
Cross-domain state changes use explicit service interfaces.

Invariant 7
Financial operations are idempotent.

Invariant 8
Closed accounting periods cannot be silently modified.

Invariant 9
Historical transactions preserve their original commercial facts.

Invariant 10
Business-critical events are recoverable from the outbox.

Invariant 11
Authorization is enforced server-side.

Invariant 12
Reporting cannot become the authoritative source of financial truth.

These invariants should become automated tests.

They are architectural contracts.


Testing Architecture

ERP testing should operate at multiple levels.

code
Unit Tests
    │
    ▼
Domain Tests
    │
    ▼
Integration Tests
    │
    ▼
Transaction Tests
    │
    ▼
Authorization Tests
    │
    ▼
Reconciliation Tests
    │
    ▼
End-to-End Workflows

For example:

code
Test:
Sale of 10 units

Verify:
    Order = Confirmed
    Inventory = Reserved / Deducted correctly
    Invoice = Created
    Payment = Correct
    Journal = Balanced
    Audit = Created
    Outbox = Created

One business workflow should therefore validate the entire consistency chain.


Deployment Architecture

A practical initial deployment:

code
                         Internet
                            │
                            ▼
                         WAF / CDN
                            │
                            ▼
                      Load Balancer
                            │
              ┌─────────────┼─────────────┐
              ▼             ▼             ▼
           Django-1      Django-2      Django-3
              │             │             │
              └─────────────┼─────────────┘
                            │
              ┌─────────────┼──────────────┐
              ▼             ▼              ▼
         PostgreSQL       Redis          Celery
              │                            │
              │                            ▼
              │                       Worker Pool
              │
              ▼
        Object Storage

The Django application instances should remain horizontally scalable.

PostgreSQL remains the authoritative transactional store.

Redis accelerates non-authoritative state.

Celery handles asynchronous workloads.


Database Strategy

The initial architecture should prefer one PostgreSQL cluster with clear domain ownership.

code
PostgreSQL
    │
    ├── Identity Tables
    ├── Sales Tables
    ├── Inventory Tables
    ├── Procurement Tables
    ├── Accounting Tables
    └── Audit / Outbox Tables

The logical boundaries are stronger than the physical table separation.

As scale increases:

code
One PostgreSQL
      │
      ▼
Read Replicas
      │
      ▼
Partitioning
      │
      ▼
Domain-specific storage

Only move toward separate databases when operational or scaling requirements justify the complexity.


High Availability

Because BizNex becomes the operational backbone of a company:

code
BizNex Down
   │
   ├── Sales Stops
   ├── Purchasing Stops
   ├── Inventory Stops
   ├── Billing Stops
   └── Reporting Degrades

Therefore high availability matters.

The architecture should eventually support:

code
Multiple Application Instances
        │
        ▼
Load Balancer
        │
        ▼
Highly Available PostgreSQL
        │
        ▼
Redis HA
        │
        ▼
Multiple Celery Workers

But high availability should not be introduced by sacrificing transactional correctness.


Disaster Recovery

Critical ERP data includes:

code
Journal Entries
Inventory Movements
Orders
Invoices
Payments
Purchase Orders
Tenant Configuration
Audit Events

Backups should therefore be:

code
Encrypted
Automated
Versioned
Access-controlled
Off-site
Regularly restored in testing

The recovery plan should explicitly define:

code
RPO
Recovery Point Objective

RTO
Recovery Time Objective

For a financial system, those numbers should be business decisions, not merely infrastructure defaults.


Security and Financial Integrity

ERP security is not only about preventing unauthorized login.

It is about preventing unauthorized state transitions.

For example:

code
Unauthorized User
       │
       ▼
Cannot approve PO
       │
       ▼
Cannot create payable commitment
       │
       ▼
Cannot trigger financial liability

Authorization therefore protects business integrity.


Separation of Duties

Enterprise ERP systems often require separation of duties.

For example:

code
Employee A
Creates Purchase Order

Employee B
Approves Purchase Order

Employee C
Processes Payment

The same person should not necessarily be able to perform all three actions.

The authorization engine can enforce:

code
creator != approver
approver != payment_processor

for sensitive workflows.

This turns RBAC into an actual governance mechanism rather than simply hiding UI buttons.


Approval Policy Architecture

Approval rules should eventually become configurable.

Example:

code
Purchase Amount
       │
       ▼
< $1,000
       │
       ▼
Manager Approval

$1,000 - $10,000
       │
       ▼
Manager + Finance

> $10,000
       │
       ▼
Manager + Finance + Director

The workflow engine should evaluate:

code
Amount
Organization
Department
Requester
Supplier
Risk Level
Budget

rather than hardcoding every threshold inside controller code.


Budget Control

Procurement can eventually integrate budget management.

code
Department Budget
       │
       ▼
Purchase Request
       │
       ▼
Committed Amount
       │
       ▼
Available Budget

Example:

code
Annual Budget       $100,000
Committed           $72,000
Actual Spend        $18,000
Available            $10,000

A purchase request exceeding available budget can require escalation or rejection.

This is where procurement, accounting, and organizational policy converge.


ERP as a System of Record

The architecture ultimately produces several authoritative ledgers:

code
Financial Truth
    └── Accounting Ledger

Inventory Truth
    └── Stock Movement Ledger

Commercial Truth
    └── Orders / Invoices

Procurement Truth
    └── Purchase Lifecycle

Identity Truth
    └── Organization / Membership / Authorization

Reports are projections of these truths.

Dashboards are projections.

Caches are projections.

Search indexes are projections.

The source of truth remains the underlying transactional domain.


Architecture Evolution

The long-term architecture can evolve without abandoning the initial design.

code
                    Phase 1
               Modular Monolith
                      │
                      ▼
             Explicit Domain Boundaries
                      │
                      ▼
              Transactional Outbox
                      │
                      ▼
               Domain Events
                      │
                      ▼
             Reporting Projections
                      │
                      ▼
              Async Workflows
                      │
                      ▼
          Independently Scalable Domains
                      │
                      ▼
              Distributed Services
                      │
                      ▼
               Saga / Compensation

The important point is that microservices are an evolution of the architecture, not the architecture itself.


What I Would Build Today

If I were building BizNex OS today, I would start with:

code
Django
    │
    ├── Explicit domain modules
    ├── Domain services
    ├── Application services
    ├── Repository boundaries
    └── Authorization policies

PostgreSQL
    │
    ├── Transactional system of record
    ├── Accounting ledger
    ├── Inventory movements
    └── Outbox

Redis
    │
    ├── Cache
    ├── Rate limiting
    └── Short-lived state

Celery
    │
    ├── Reports
    ├── Notifications
    ├── Reconciliation
    └── Async workflows

Object Storage
    │
    ├── Documents
    ├── Reports
    └── Generated artifacts

The architecture would remain a modular monolith until there is a measurable reason to distribute a domain.


What I Would Avoid Initially

Avoid Microservices Everywhere

code
Sales Service
Inventory Service
Accounting Service
Billing Service
Procurement Service

before the business has enough scale to justify the operational complexity.


Avoid Mutable Financial Balances as Truth

code
account.balance += amount

should never be the fundamental accounting mechanism.


Avoid Mutable Inventory as the Only Source of Truth

code
product.stock_quantity = 47

without a movement history makes reconciliation difficult.


Avoid Direct Cross-Domain Database Access

code
Sales
   │
   └── UPDATE inventory_stock

creates hidden coupling.

Prefer:

code
Sales
   │
   ▼
Inventory Service

Avoid Synchronous External Dependencies in Core Transactions

Do not make:

code
Create Order
   │
   ▼
Send Email
   │
   ▼
Generate PDF
   │
   ▼
Call External ERP
   │
   ▼
Commit Database

the critical transaction path.

Instead:

code
Core Transaction
      │
      ▼
Commit
      │
      ▼
Outbox
      │
      ▼
Async Workers

The Architectural Contract

The most important output of BizNex OS is not an invoice.

It is consistent business state.

A business operation should be able to answer:

code
What happened?
      │
      ▼
Which organization?
      │
      ▼
Which user initiated it?
      │
      ▼
Which domain owns the operation?
      │
      ▼
Which inventory changed?
      │
      ▼
Which financial accounts changed?
      │
      ▼
Which documents were created?
      │
      ▼
Which events were emitted?
      │
      ▼
Can the entire operation be reconstructed?

That is what makes an ERP trustworthy.


Final Architecture Principle

BizNex OS is not fundamentally an inventory application, accounting application, POS, or procurement application.

It is a business transaction system in which those domains operate against a shared set of authoritative facts.

code
                         BizNex OS
                             │
        ┌────────────────────┼────────────────────┐
        ▼                    ▼                    ▼
      Sales              Inventory            Procurement
        │                    │                    │
        └────────────────────┼────────────────────┘
                             ▼
                        Accounting
                             │
                             ▼
                       Business Truth
                             │
        ┌────────────────────┼────────────────────┐
        ▼                    ▼                    ▼
     Reporting             Audit              Operations

The deepest architectural principle is:

Every important business fact should have one authoritative owner, every state transition should preserve that domain's invariants, and every cross-domain workflow should be explicitly orchestrated and traceable.

status: blueprint_loaded

$ ls related_articles

status: end_of_file

Related Blueprints

Continue exploring related architecture patterns.

BizNex OS (Unified Business Operating System)

Enterprise-grade business operating system designed to unify inventory, accounting, sales, procurement, billing, and organizational workflows within a secure multi-tenant SaaS architecture.

01Double-entry accounting core
02Ledger-based inventory engine
03Domain-bounded ERP modules
04Tenant-isolated financial records
05Event-ready modular monolith

AccessCore IAM (Enterprise Identity & Access Management Platform)

Enterprise-grade identity and access management platform designed to centralize authentication, authorization, user lifecycle management, RBAC governance, and organizational security controls.

01Multi-tenant identity core
02Hierarchical RBAC engine
03SSO & token architecture
04Audit-ready security logging
05Delegated administration model

Event Management & Ticketing System

Scalable event management platform supporting event lifecycle management, ticket sales, attendee registration, payment processing, and QR-based access control.

01Concurrency-safe ticket booking
02Booking-first reservation model
03Webhook-driven payment verification
04Event lifecycle state machine
05QR-based check-in validation