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}}
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
System Design
Architecture
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
Data
Frontend
Docs / Ops
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}}