User
Responsibilities
- Authenticate with bcrypt password
- Hold role: buyer, seller, driver or admin
- Track rating average and review count
- Accept current terms version
Collaborators
- Listing
- Order
- Review
- Courier
- TermsAcceptance
CRC, EERD, Context, Level 1 DFD, Use case and the physical schema for v1.1.
The six design artefacts required for D2 2.2, plus the order workflow. Every diagram on this page is rendered as HTML and CSS so it stays readable on a mobile screen and prints cleanly. Reflects the v1.1 codebase: 62 MySQL tables, courier dispatch, escrow with 48 hour auto-release, community and feed, swaps, bids, lay-bye, and pickup at PEP, Pargo and Paxi.
Class, Responsibilities, Collaborators for the 20 most active domain classes in the v1.1 build. Index-card layout so each card fits a single mental chunk.
Core 20 entities and their relationships, in a tabular EERD style. Primary keys are highlighted, foreign keys are blue. Relationship lines are noted under each card.
1 to many: listings, orders (as buyer or seller), reviews, follows.
Self-referencing tree. 1 to many: listings.
1 to many: listing_images, listing_media, offers, bids. Many to many: wishlists.
Many to one: listings.
Many to one: listings. Drives the feed.
One cart per user. Many items per cart.
1 to many: order_items. 1 to 1: escrow_transactions, delivery_assignments.
Many to one: orders. Snapshots protect against price edits after sale.
1 to 1: orders. Watched by escrow_release cron.
1 to 1: users. 1 to many: delivery_assignments, driver_earnings.
1 to 1: orders. 1 to many: assignment_legs.
Two per order: one for pickup handover, one for delivery handover.
1 to many: dispute_evidence. Freezes escrow on open.
1 to many: community_members, community_posts.
1 to many: post_comments, post_reactions.
1 to many: bids. AuctionCloseCron awards top bidder.
1 to many: laybye_payments. Reserves the listing.
Many to one: listings. Accepted offer becomes an order.
Watch list with optional price drop alert.
One per order per direction (buyer to seller, seller to buyer).
Me2You as a single process with all real external entities and the labelled data flows. The system at the centre is everything inside m2y.online.
Real processes for v1.1, with their data stores. Sixteen processes total. Stores are keyed Dn so they cross-reference the schema.
Five actors and the use cases each performs. "Include" calls are noted in italics; "extend" calls are noted with a plus sign.
The 62 physical tables in db/schema.sql, grouped by domain so the marker can navigate. The key columns of the most important tables are shown in full.
Identity, addresses, sessions, terms acceptance, password resets.
Listings and their images, video media, categories, comments and reactions.
Carts, orders, line items, escrow ledger, refunds, payment audit.
Courier roster, on-duty pings, assignments, legs, OTPs at pickup and delivery, pickup-point shipments, earnings and payouts, GPS telemetry.
driver_pings GPS telemetry trail for delivery ETA learning.
Reviews, disputes and their evidence, audit trail, cached trust scores.
user_risk_scores Cached trust and risk score per user (feed, fee and escrow perks).
Communities, members, posts, comments and reactions on posts.
Counter-offers and swaps, auctions and bids, lay-bye plans and instalments, wishlists, saved searches, referrals.
Direct messages, notifications, reports.
Feature flag rollouts and event capture for A/B style toggles.
users (key columns):
| Column | Type | Notes |
|---|---|---|
id | BIGINT PK | auto increment |
email | VARCHAR(190) UNIQUE | lowercased on insert |
password_hash | CHAR(60) | bcrypt cost 12 |
display_name | VARCHAR(80) | public |
role | ENUM('buyer','seller','driver','admin') | defaults to 'buyer' |
status | ENUM('active','suspended','deleted') | FK guarded |
rating_avg | DECIMAL(3,2) | denorm cache |
persona | ENUM('seller','collector','hobbyist','specialist','service') | profile flair |
created_at | TIMESTAMP | defaults NOW |
orders (key columns):
| Column | Type | Notes |
|---|---|---|
id | BIGINT PK | auto increment |
reference | CHAR(12) UNIQUE | public reference, e.g. M2YAB12CD34 |
buyer_id / seller_id | BIGINT FK users.id | buyer plus seller (single seller per order) |
subtotal_cents | INT | sum of line items |
delivery_cents | INT | flat per method |
tip_cents | INT | 0, 1000, 2000 or 5000 |
protection_cents | INT | buyer protection fee from settings |
total_cents | INT | server-side recomputed at PayFast redirect |
fulfillment_type | ENUM | driver_door_to_door, pickup_network, self_collect_inspect, cash_on_delivery |
status | ENUM | placed, paid, dispatched, received, completed, cancelled, disputed |
pf_payment_id | VARCHAR(64) | set by ITN handler |
escrow_transactions (key columns):
| Column | Type | Notes |
|---|---|---|
id | BIGINT PK | |
order_id | BIGINT UNIQUE FK orders.id | 1 to 1 |
amount_cents | INT | buyer total, ex driver tip |
status | ENUM('held','released','refunded','frozen') | state machine |
release_due_at | DATETIME | set 48h after delivered_at |
released_at | DATETIME NULL | set by cron |
payout_ref | VARCHAR(64) NULL | AUTO-{order.reference} for cron releases |
couriers (key columns):
| Column | Type | Notes |
|---|---|---|
id | BIGINT PK | |
user_id | BIGINT UNIQUE FK users.id | 1 to 1 with user |
on_duty | TINYINT(1) | 1 if heartbeat in last 5 min |
home_lat / home_lng | DECIMAL(9,6) | radius centre |
radius_km | INT | 10, 20 or 30 |
status | ENUM('pending','approved','suspended') | admin gated |
vehicle_type | ENUM('foot','bike','car','bakkie','van') |
delivery_assignments (key columns):
| Column | Type | Notes |
|---|---|---|
id | BIGINT PK | |
order_id | BIGINT UNIQUE FK | 1 to 1 |
courier_id | BIGINT FK couriers.id | nullable until accepted |
status | ENUM('open','accepted','picked_up','delivered','failed','cancelled') | |
payout_cents | INT | base plus per km |
accepted_at ... delivered_at | DATETIME | timestamps per leg |
communities (key columns):
| Column | Type | Notes |
|---|---|---|
id | BIGINT PK | |
slug | VARCHAR(80) UNIQUE | e.g. soweto-thrift |
name | VARCHAR(120) | |
suburb | VARCHAR(80) | denorm for search |
manager_id | BIGINT FK users.id | community manager |
visibility | ENUM('open','private') | |
member_count / post_count | INT | cached counts |
auctions (key columns):
| Column | Type | Notes |
|---|---|---|
id | BIGINT PK | |
listing_id | BIGINT UNIQUE FK | |
start_cents / reserve_cents | INT | min and reserve |
current_bid_cents | INT | cached top bid |
top_bidder_id | BIGINT FK users.id NULL | |
ends_at | DATETIME | auction_close cron checks |
status | ENUM('live','closed','cancelled') |
laybye_plans (key columns):
| Column | Type | Notes |
|---|---|---|
id | BIGINT PK | |
listing_id | BIGINT FK | |
buyer_id | BIGINT FK users.id | |
total_cents / paid_cents | INT | balance is total minus paid |
instalment_cents | INT | weekly |
weeks | INT | typical 4 to 12 |
status | ENUM('pending','approved','active','completed','defaulted','cancelled') |
offers (key columns):
| Column | Type | Notes |
|---|---|---|
id | BIGINT PK | |
listing_id | BIGINT FK | |
buyer_id | BIGINT FK users.id | |
amount_cents | INT | counter price |
swap_listing_id | BIGINT FK listings.id NULL | set for a swap proposal |
status | ENUM('pending','accepted','rejected','expired','withdrawn') | |
expires_at | DATETIME | 48h default |
State transitions on the orders table. Each arrow names the trigger and the side effect (escrow, dispatch, payouts).
rands() helper in app/config/money.php.order_items at checkout so a seller editing the listing price tomorrow cannot retroactively change a buyer's invoice.release_due_at. The cron/escrow_release.php job runs every 5 minutes, takes a leader lock, then settles up to 200 ready rows in a transaction. Disputes that are still open block release.held to frozen in the same transaction. The cron skips frozen rows; only an admin resolution can release or refund._checkout_idem) to prevent double-submit. PayFast ITN uses webhook_idempotency to refuse a replayed pf_payment_id.