Skip to content
Architecture

How to Build a Scalable ERP Architecture

A practical architecture for modular ERP platforms: bounded contexts, transactional outbox, ledger-based inventory, and a migration path away from monolithic suites.

Author
Omar Al-Masri
Published
Jun 18, 2025
Reading time
11 min read
Topic
Architecture

Model the Business as Bounded Contexts

Most ERP failures are modelling failures, not infrastructure failures. Before selecting a database or a message broker, split the platform into bounded contexts with explicit ownership: finance and the general ledger, inventory, procurement, manufacturing, order management, and workforce. Each context owns its tables, publishes domain events, and exposes a narrow API. The moment two teams write to the same table, every release becomes a coordinated negotiation. Draw the seams first, then decide which contexts can share a process and which must deploy independently.

Ask which invariants must hold inside one transaction. Stock reservation, ledger posting, and payment capture each need atomicity within their own context, but rarely across contexts. Cross-context consistency belongs in sagas with compensation steps. Name the aggregate roots, define the commands that mutate them, and record the events they emit. That discipline keeps the architecture explainable long after the original team has moved on, which is precisely when new engineers need a reliable map.

  • Finance owns journals and balances; inventory owns quantity on hand and reservations.
  • Publish versioned domain events instead of sharing database views across contexts.
  • Keep exactly one context responsible for each aggregate, even if it means a duplicated read model.
  • Treat reporting as a downstream consumer that builds its own denormalised store.

Integrate with a Transactional Outbox

Publishing an event after committing a database transaction is the classic dual-write bug: the commit succeeds and the broker call fails, or the message is sent and the transaction rolls back. The transactional outbox removes the ambiguity. The service writes the domain change and the outbound message inside the same local transaction, then a relay reads unpublished rows in sequence and forwards them to the broker. Ordering is preserved per aggregate as long as the relay reads by an increasing sequence column.

Relays must be idempotent and safe to restart. Delivery is at-least-once, so consumers deduplicate on an idempotency key carried in the message envelope. Give the outbox table a retention policy of seven days and archive older rows to object storage. Partition the relay by aggregate key when throughput grows, and publish a dead-letter topic for messages that fail validation three times in a row.

ts
// Domain write and outbox row share one transaction.
await db.transaction(async (tx) => {
  await tx.insert(orders).values(order);
  await tx.insert(outbox).values({
    topic: "orders.placed",
    aggregateId: order.id,
    payload: order,
    occurredAt: new Date(),
  });
});

Keep Inventory Truthful with Ledger Tables

A mutable quantity_on_hand column is convenient until two processes decrement it at once. Model inventory as an append-only ledger of movements: receipts, issues, transfers, adjustments, and reservations, each with a signed quantity and a source document. The current balance becomes a projection over those rows. Historical questions such as why the count changed on a given afternoon become answerable without a separate audit trail.

At roughly 40,000 movement lines a day, a warehouse reaches ten million rows a year, which a well-indexed ledger can serve comfortably. Maintain a nightly snapshot per location and stock keeping unit so balance queries stay constant-time, then replay movements written since the snapshot. Reconciliation compares the ledger projection against physical counts and records variances rather than silently overwriting them.

  • Index the ledger on (location_id, sku_id, occurred_at) for balance and drill-down queries.
  • Reserve stock with a short-lived expiry so abandoned carts release inventory automatically.
  • Never allow negative stock as a database constraint; represent oversell as an exception record.
  • Emit a movement event for every write so downstream analytics stay current.

Scale Reads Before Scaling Writes

ERP workloads are usually read-heavy: dashboards, availability checks, and month-end reports dwarf order entry. Add read replicas first, route reports to them explicitly, and measure replication lag before trusting a replica for operational screens. A lag of two seconds is fine for a margin report and unacceptable for an availability check during checkout. Separate the two paths in code rather than hoping a connection pool will sort it out.

Indexes matter more than hardware. Covering indexes for the ten most frequent queries often remove a full table scan worth several seconds. Partition large tables by month so retention becomes a metadata operation and vacuum stays bounded. Replace offset pagination with keyset pagination: fetching page 500 with OFFSET 10000 reads and discards ten thousand rows, while WHERE id > $last reads only the page that is returned.

Sequence the Migration, Not Just the Rewrite

A big-bang ERP replacement concentrates risk into one cutover weekend. A strangler approach moves one bounded context at a time behind a facade, starting with the least entangled: procurement, then inventory, then order management, and finally finance. During coexistence, change data capture keeps the legacy system and the new context aligned, and reconciliation reports compare totals daily until the difference stays at zero for a full accounting period.

Wrap each migrated capability in a feature flag so a rollback is a configuration change rather than a deployment. Budget one context per quarter, not per month; the integration work, data migration, and user training dominate the calendar. Publish a migration dashboard showing records moved, conflicts resolved, and open data-quality issues. The destination is not a finished rewrite but a platform that keeps shipping after the consultants leave.

Omar Al-Masri

Founder & Principal Architect · Dubai, UAE

Distributed systems, platform strategy, technical due diligence

Next step

Have a similar challenge?

If this article maps to a problem on your roadmap, we can walk through the trade-offs against your constraints and tell you what we would do first.

Reply within one business day
Scoped proposal, fixed discovery
NDA and security review welcome