Web / SaaS

How We Built a Courier Management SaaS: The ShipAsaan Architecture Breakdown

Mohsin MalikMohsin Malik · Co-Founder & Data Architecture LeadJuly 18, 2026Updated July 18, 20262 min read

ShipAsaan is a courier management platform we built end to end. A NestJS and PostgreSQL core serves seven product surfaces: a merchant portal, an internal operations portal, customer and courier Flutter apps, WooCommerce and Shopify integrations, and a public REST API. The architecture bet was a modular monolith built around one canonical shipment lifecycle. Every surface is a different view over the same state machine, and that's what kept seven surfaces shippable by a small senior team.

What does a courier platform actually have to do?

Courier operations before ShipAsaan ran on spreadsheets, phone calls, and disconnected tools. There was no single view of a parcel from booking to delivery, tariff and billing calculations were manual, rider incentives were hand-computed, and merchant onboarding was scattered across chat threads. The product brief reduced to one sentence. Make every parcel, rupee, and role visible in one system.

Why a modular monolith instead of microservices?

With shipments, billing, incentives, dispatch, support, and integrations all transacting against the same lifecycle, splitting into services early would have bought us distributed-systems problems before we had scale problems. We chose a modular NestJS monolith over PostgreSQL, with clear module boundaries (shipments, tariffs, incentives, portals, integrations), one transactional core, and the option to peel modules into services later. That decision is why seven surfaces launched instead of three.

How does the shipment lifecycle drive everything?

The heart of the system is a state machine covering booking, pickup, hub transfers, out-for-delivery, and then delivered, returned, or exception, with consignment notes carrying QR and barcodes. Every other feature hangs off state transitions. Real-time notifications fire on them, the billing engine prices them (zones, weight slabs, COD handling, company-specific rates), the incentive engine accrues rider and sales payouts from them, and audit logs record who moved what and when. New features stayed cheap because they subscribe to the lifecycle instead of reimplementing it.

What did the mobile and integration layer look like?

  • Two Flutter apps built from shared patterns: a customer app for tracking and notifications, and a courier app for delivery workflows and status updates. One team, both stores.
  • Firebase push notifications wired to lifecycle transitions instead of polled.
  • A WooCommerce plugin and a Shopify integration, so merchant stores create shipments automatically.
  • A public REST API, the same contract our own portals use, so third-party merchants can integrate without special-casing.

Which tradeoff mattered most?

Automated billing was the riskiest module. Tariffs vary by zone, weight slab, service type, COD, insurance, and per-company negotiated rates. The temptation was a rules engine with a UI. The discipline was to version tariff configurations as data, compute deterministically, and log every calculation input alongside the output. When a merchant disputes an invoice, operations can replay exactly how a number was produced. Billing disputes went from arguments to lookups.

Want this applied to your business?

Tell us what you're building. We'll tell you how long it takes and what it costs — for free, in plain English.

No agency jargon. No surprise invoices. Just engineers who give you a straight answer.

Chat on WhatsApp