# Architecture

## Stack decision

Laravel 11 + Livewire 3, single codebase. React + separate API was
considered and dropped — for a solo/two-person team building an unvalidated
MVP, running two codebases (React SPA + Laravel API) adds auth-token
handling, CORS, and duplicate build tooling for no current benefit. Revisit
only if a real requirement shows up that Livewire can't handle (e.g. a
genuinely offline-capable mobile app with local-first sync) — not because
React is more common.

## Multi-tenancy

Single database, tenant-scoped by `tenant_id` on every tenant-owned table
(not separate databases per tenant — unnecessary operational overhead at
this scale, and migrations become painful across N databases).

Enforce tenant scoping with a global Eloquent scope
(`BelongsToTenant` trait + `TenantScope`), not ad-hoc `where()` calls
scattered through controllers/Livewire components. A single missed
`where('tenant_id', ...)` is a data leak between contractors — this is the
one place in the app where a bug is a trust-destroying incident, not just
an inconvenience.

## Auth

- No public self-registration in MVP. Contractor accounts are created by a
  super admin (you), matching the stated plan.
- Login via mobile number OR email — use Laravel's standard auth guard with
  a custom `username()` resolver that checks which format was entered.
- Roles (MVP): `owner` (the contractor) and `supervisor` (site-level user,
  if the contractor delegates a site to someone else). Don't build a
  granular permissions system yet — two roles is enough until a contractor
  actually asks for more.

## Structure

```
app/
  Models/          Tenant, Project, InventoryItem, InventoryTransaction,
                    CashReceipt, Expense, User
  Livewire/
    Dashboard/
    Projects/       ProjectList, ProjectForm, ProjectDetail
    Inventory/       InventoryList, InventoryTransactionForm
    Finance/          CashReceiptForm, ExpenseForm, ProfitLossView
  Policies/         ProjectPolicy, etc. — enforce tenant + role checks here,
                    not inline in Livewire components
database/
  migrations/
resources/
  views/livewire/
```

## Background jobs / backup

"Automated data backup" was in the original notes. For MVP: a scheduled
Laravel command (`php artisan backup:run` via `spatie/laravel-backup`) that
dumps the DB nightly to a remote disk (S3-compatible or similar). Don't
build custom backup tooling.

## PWA

Add a manifest + service worker for installability and caching of static
assets. Do NOT attempt offline write support (queued inventory/cash entries
syncing later) in MVP — that's a genuinely hard problem (conflict
resolution, partial sync failures) and isn't in the confirmed feature
scope. If sites have poor connectivity, that's a real risk worth asking the
contractor about before committing to solve it.

## What's still an open decision

- Hosting target (shared hosting vs VPS vs managed platform) — not decided.
- Whether "vendor payments" need vendor as a first-class entity (recurring
  vendors, vendor-level totals) or are just a free-text field on Expense.
  Schema below treats vendor as free text for MVP; revisit if contractors
  want vendor-level reporting.
