Event Model
Asset facts should be append-only. State changes are represented by new events, not edits.
Principles
- Every user-confirmed action creates an event.
- Events are immutable after creation.
- Corrections and tag replacements are new events.
- Events carry client-minted IDs so retries are safe.
- Backend ingest must deduplicate by event ID.
Target Sync Contract
POST /api/v1/events/sync
Content-Type: application/json
Authorization: Bearer <token>
{
"events": [
{
"event_id": "018f4b7e-...",
"event_type": "ASSET_ONBOARDED",
"asset_code": "AST-0001",
"origin": "mobile",
"actor": "user-id",
"occurred_at": "2026-09-01T05:30:00Z",
"payload": {}
}
]
}
Expected response:
{
"received": 12,
"inserted": 10,
"duplicates": 2
}
Backend Ingest Rule
INSERT INTO asset_event (...)
VALUES (...)
ON CONFLICT (event_id) DO NOTHING;
POC Event Types
| Event | Meaning |
|---|---|
DELIVERY_RECEIVED | Delivery accepted |
DELIVERY_REJECTED | Delivery rejected |
ASSET_ONBOARDED | Unit committed to inventory |
GAN_ATTACHED | Goods acceptance note captured |
GAN_DEFERRED | Goods acceptance note missing at commit |
ASSET_CORRECTED | Post-submit field correction |
TAG_REPLACED | Temporary or missing tag replaced |
ASSET_RECEIVE | Legacy receive event |
ASSIGN_CONFIRM | Legacy assignment confirmation |
Queue Behavior
flowchart TD
A["User confirms action"] --> B["Create immutable event"]
B --> C["Insert into local queue"]
C --> D{"Network available?"}
D -- No --> E["Remain pending"]
D -- Yes --> F["POST unsynced batch"]
F --> G{"Backend accepts?"}
G -- Yes --> H["Mark events synced"]
G -- No --> E
Current POC Details
- Queue database:
asset_event_queue.db. - Queue table:
asset_event_queue. - Local database version:
2. - Current migration renames payload key
valuetounit_price. - Backend URL is hard-coded in
reference/mobile_app_demo/lib/services/sync_service.dart.