Freight and logistics marketplace for Nigeria — Flutter carrier and shipper apps, Laravel 13 API, and a dispatch console
# Haulage platform
A freight and logistics marketplace for Nigeria: two mobile apps for the people
moving goods, an API behind them, and a dispatch console for the brokerage in
the middle.
It began as a purchased white-label ride-hailing codebase and is being rebuilt
into something that models freight properly. A taxi has a passenger, a pickup
and a destination. A load has cargo with weight and packaging, several stops,
paperwork that expires, a price the customer pays and a different price the
carrier is paid — and a truck that sits at a gate for four hours while nobody
records it.
## What is here
```
logistics_flutter/
packages/core/ domain types, and the client for both API generations
apps/carrier_app/ for the haulier: loads offered, bidding, running a job,
proof of delivery, offline queue, background tracking
apps/shipper_app/ for the customer: post a load, compare bids, award,
track, re-book a past run
haulage_api/ Laravel 13 / PHP 8.3 — the API and the dispatch console
backend/seed/ dev seed for the inherited backend being replaced
```
**145 tests** — 76 Dart, 69 PHP.
## Design decisions worth knowing
**The domain speaks logistics, never ride-hailing.** The inherited backend
called a carrier a `provider` and a load a `trip`. Every one of those words is
confined to `packages/core/lib/src/config` and the mappers in `src/data`.
Replacing the backend did not require changing a single domain type or screen —
the diff was confined to those two directories, which is the point.
**Both prices are stored.** The old schema had one price field, so the margin a
brokerage lives on had to be inferred from whichever bid won. `loads` carries
`shipper_price` and `carrier_price`, and margin is a subtraction. A carrier is
never sent either the margin or what they are being paid relative to it.
**Status is a transition, not a field.** The server refuses an illegal jump
rather than storing whatever arrives. This matters because the carrier a …