Devizur
All insightsE-commerce

Connecting E-commerce, POS, Inventory and Admin Systems Into One Platform

How to make customer-facing and staff-facing applications operate as different views over one set of products, prices, orders, stock and permissions.

Soud Al Raihan4 March 20263 min read
CustomerCashierManagerWebPOSAdminShared PlatformOrdersInventoryPricing

History and context

1990s
Retail systems were usually store-centred; e-commerce later emerged as a separate channel with separate catalogues and stock models.
2000s–2010s
Omnichannel expectations forced retailers to connect web, POS, loyalty, fulfilment and inventory.
Today
The strongest systems treat channels as experiences over shared business capabilities rather than separate businesses connected by nightly sync.

How fragmentation happens

A company rarely designs five sources of truth. It accumulates them. The online store owns product data, POS stores another price list, warehouse staff maintain stock separately, and administration tools add another configuration layer. Each application is useful, but the combined system becomes expensive to reconcile.

E-commercePOSAdminOperationsASP.NET Core APICatalogPricingOrdersInventoryPostgreSQLRedis
Channel applications should converge on shared domain services.

Define domain ownership first

DomainOwns
CatalogProducts, categories, attributes, sellability
PricingBase prices, promotions, eligibility rules
InventoryBalances, reservations, movements
OrdersOrder lifecycle, lines, totals
FulfilmentPicking, preparation, dispatch
IdentityUsers, roles and permissions

Once ownership is explicit, APIs become much easier to design because each endpoint has a domain reason to exist.

One price engine, many channels

Promotions should not be reimplemented in each frontend.

C#
public PriceResult Calculate(
    IReadOnlyCollection<CartLine> lines,
    IReadOnlyCollection<SalesOffer> offers)
{
    var result = PriceResult.From(lines);

    ApplyProductOffers(result, offers);
    ApplyCombinedTargetOffers(result, offers);
    ApplyOrderOffers(result, offers);

    return result;
}

The website, POS and admin quotation screens can all call this same capability. A campaign configured once therefore behaves consistently across channels.

Inventory is a ledger, not a number

Real inventory usually distinguishes on-hand, reserved, available, damaged, in-transfer and allocated quantities. A movement ledger preserves why stock changed.

PostgreSQL
CREATE TABLE inventory_movement (
    movement_id      BIGSERIAL PRIMARY KEY,
    product_id       BIGINT NOT NULL,
    location_id      BIGINT NOT NULL,
    movement_type    VARCHAR(30) NOT NULL,
    quantity_delta   NUMERIC(18,3) NOT NULL,
    reference_type   VARCHAR(30),
    reference_id     BIGINT,
    created_at       TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

Reliable checkout and the outbox pattern

Checkout should secure the critical business transaction first. Email, CRM sync and non-critical reporting should happen afterwards.

BrowserCheckout APIOrder + PaymentOutboxEmailFulfilmentCRM
Non-critical integration work moves off the checkout critical path.
C#
public sealed class OutboxMessage
{
    public Guid Id { get; init; }
    public DateTimeOffset OccurredAt { get; init; }
    public string Type { get; init; } = default!;
    public string Payload { get; init; } = default!;
    public DateTimeOffset? ProcessedAt { get; set; }
}

Real-time operations without making WebSockets the database

Kitchen screens, station boards and fulfilment dashboards benefit from server-pushed updates. ASP.NET Core SignalR is designed for this class of real-time web functionality. The client should still refetch authoritative state after reconnecting, because connection events are notifications rather than durable truth.

C# / SignalR
await hubContext.Clients
    .Group($"shop:{shopId}:station:{stationId}")
    .SendAsync("taskUpdated", task, cancellationToken);
React / TypeScript
connection.on("taskUpdated", task => {
  setTasks(current =>
    current.map(x => x.id === task.id ? task : x)
  );
});

Menu visibility is not security

Frontend permission checks improve usability, but API authorisation remains authoritative.

C#
[Authorize(Policy = "ORDER_REFUND")]
[HttpPost("orders/{orderId:long}/refund")]
public Task<IActionResult> Refund(long orderId)
{
    // Server-side permission enforcement.
}

Business value and practical considerations

Benefits

  • One commercial model
  • One order lifecycle
  • Better reporting
  • Lower reconciliation effort
  • True omnichannel workflows

Practical considerations

  • Shared platform becomes critical infrastructure
  • Domain boundaries require discipline
  • Migration must be staged carefully
  • Operational monitoring matters more
SA
Soud Al RaihanCommerce systems and backend architecture · Devizur

References