</>longpham.tech
All case studies
LogisticsConfidential

Cross-Border Logistics Platform

Production cross-border shipping platform for US–Vietnam parcels with a date-driven shipment workflow, async SMS/email notifications and compliance-grade audit trails on a monolithic Laravel core.

Client
Confidential · Logistics
Role
{{TODO: e.g. Solution Architect / Lead Backend Engineer}}
Period
{{TODO: e.g. 2017–2024}}
5-state shipment workflow derived from date milestones, no explicit state table
Async queue-based SMS + email decoupled from the request path
Region-aware SMS drivers (US vs VN phone logic)
Soft deletes + immutable audit logs for retention and restore
Address denormalization copied into orders for shipping immutability

Summary

Overview

The platform manages US–Vietnam cross-border parcels end to end: pickup, warehouse consolidation into flight batches, and final-mile delivery in Vietnam. Customers — primarily the Vietnamese diaspora in the US — create orders online and track shipments publicly, while admin staff run bulk CRUD, batch status changes and document generation (PDF, thermal labels).

The architectural through-line is keeping a genuinely complex logistics domain simple: a shipment's state is derived from date milestones rather than a separate state machine, notifications run async off the request path, and compliance is met with soft deletes plus append-only audit logs instead of a heavier records subsystem.

Context

The Problem

Cross-border parcel shipping carries real domain complexity — multi-leg movement across two countries, region-specific notification rules, and legal retention obligations — but the business needed fast feature delivery at mid scale, not a distributed rewrite.

Constraints

  • A shipment moves through several stages (US receipt → flight → VN receipt → delivery); the model must reflect this without an over-engineered state machine.
  • Customer notifications span two regions with different phone conventions; US and VN SMS must route differently.
  • Notification providers (SMS/email) are slow and occasionally fail; they must never block a shipment status update.
  • Vietnamese tax/audit expectations require retaining deleted orders and preserving an immutable trail of status transitions.
  • Addresses change over time, but a shipment's destination must stay locked to what it was at order time.

Scope

Requirements

Functional

  • Public tracking by tracking/package number, no auth required
  • Online order entry via customer web form
  • Admin shipment CRUD, CSV bulk import/export, batch status changes, trash/restore
  • Master grouping of shipments into flight batches tracked as a unit
  • Document generation: PDF export, thermal label printing, bulk print-on-demand
  • Status notifications by email and SMS on key milestones
  • Customer address book with a default address per customer
  • Customer blacklist management for fraud prevention
  • Money and COD (cash-on-delivery) tracking fields

Non-functional

  • Async notifications — queue-based so email/SMS never block request–response
  • Region-aware SMS drivers with US and VN phone-specific logic
  • Soft deletes for data retention, audit and restore rather than physical deletion
  • Redis cache layer via Predis for read performance
  • Daily backup strategy via spatie/laravel-backup
Built for roughly 1K–10K active customers on a single-brand, single-region deploymentSized to scale toward 100K+ orders per month without re-architecture~20 domain models across 21 migrations, iterated in production over multiple yearsMonolithic Laravel behind Nginx with Redis-backed queue workers

System Design

Architecture

100% · drag to pan · scroll to zoom
Rendering diagram…

Order & Shipment Service

Owns the hoadons/online_orders lifecycle; the shipment's state is derived from date milestone fields (US receipt, flight, VN receipt, delivery) rather than a separate status table.

Public Tracking Service

Unauthenticated lookup by tracking/package number, Redis-cached to keep read-heavy customer traffic off MySQL.

Notification Queue

Dispatches SMS/email jobs onto a Redis queue so provider latency and failures never block a shipment status update.

Region-Aware SMS Drivers

Separate US and VN send paths handle country-specific phone formatting and provider selection for a bi-national customer base.

Audit & Retention Layer

Append-only status logs plus soft deletes preserve an immutable transition history and allow restore, satisfying compliance and retention needs.

Stack

Technology Stack

Backend

PHP 5.6+Laravel 5.6

Data

MySQLRedis (Predis)

Frontend

Vue.js 2.1Bootstrap 3.3BladeLaravel Mix / Webpack

Docs / Ops

milon/barcodebarryvdh/laravel-dompdfmaatwebsite/excelspatie/laravel-backupMailgun / SESNginx

Persistence

Data Model

  • hoadons — main shipment record: tracking_number, package_number, and four date milestones (ngaynhan_us, ngay_chuyen_hang, ngaynhan_vn, ngaygiao) that together imply the current state
  • online_orders — customer-submitted orders with denormalized address fields and soft-delete support
  • hoadon_masters — flight-batch groupings; shipments are aggregated and tracked as a unit
  • addresses — normalized customer address book with a default per customer
  • log_hoadons — immutable audit log of status transitions
  • online_order_jobs — audit trail of notification jobs dispatched to customers
  • users — customers and admin staff
  • blacklists — blocked/fraud customer list

Denormalization pattern: shipping address fields (name, address, city, state, zip, country, mobile) are copied into the order alongside the normalized address foreign key, so the destination is locked at order time and reads stay index-friendly.

Reasoning

Key Decisions & Trade-offs

Monolithic Laravel over services

trade-off

Layers scale together and stay coupled, but the team ships features fast and mid-scale load doesn't justify distribution.

State derived from date milestones, not a state table

trade-off

The state machine is implicit and must be read from dates, but it is simple, reversible, and reuses fields the tracking timeline already requires.

Async Redis job queue for notifications

trade-off

A queue runner must be operated, but email/SMS latency and failures never block shipment updates — critical for timely customer notifications.

Soft deletes for orders

trade-off

Data grows and every query must filter deleted rows, but retention, restore and audit obligations are met without a separate archival system.

Denormalized addresses copied into orders

trade-off

Later address edits can diverge from the order, but destinations stay immutable at ship time and lookups avoid extra joins.

Operations

Scaling & Reliability

Reads: Redis caches public tracking lookups so the highest-volume, unauthenticated traffic is served without hammering MySQL; DB read replicas are the next lever if needed.

Notifications: async jobs decouple SMS/email from request–response, and the Redis queue can be swapped for a distributed broker (SQS, RabbitMQ, Beanstalkd) as notification volume grows.

Retention & recovery: soft deletes keep history queryable without purging, and daily spatie/laravel-backup snapshots provide a recovery path. The monolith is sized to grow toward 100K+ orders/month before a service split becomes worthwhile.

What made this hard

Architect's Challenges

  • Modeling a multi-leg cross-border workflow as a simple date-derived state instead of an explicit state machine
  • Routing notifications correctly across two regions with different phone conventions
  • Keeping provider latency and failures off the shipment-update request path
  • Meeting audit/retention requirements with soft deletes plus immutable logs rather than a heavier records subsystem
  • Reducing manual admin overhead through bulk import/export, batch status changes and multi-order printing

Results

Outcome

  • Public tracking and online order entry offload routine work from support staff
  • Immutable status logs and retained soft-deleted orders provide a compliance-ready trail
  • {{TODO: add real metrics — active customers, orders/month, notification volume, uptime}}