Files
ProcureLinc-Mobile-App/docs/ARCHITECTURE.md
T
Abin ToppoandClaude Fable 5 1d7646d938
CI / quality (push) Waiting to run
feat: architecture refactor — core layer, spreadsheet engine, envelope bidding, tests
- Add src/core/ (config, errors, events, http, observability, storage, query)
- Add src/Spreadsheet/ engine with native POI backend and Excel bridge
- Add Envelope C spreadsheet flow and tender bidding envelope modules
- Consolidate components into src/components/, remove legacy Component/ and Hooks/
- Add registration state/validation, auth session guard, profile edit modules
- Add unit tests across axios, redux, bidding, registration, encryption, core
- Add CI workflow, docs (architecture, audit, DSC API spec), keystore example
- Remove tracked keystore binary; ignore scripts.zip

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 10:20:22 +05:30

154 lines
7.8 KiB
Markdown

# ProcureLinc — Architecture Guide (as-built)
This is the **as-built** companion to [`ARCHITECTURE_AUDIT.md`](./ARCHITECTURE_AUDIT.md).
The audit is the blueprint (what was wrong, what to build); this document is the
map of what now exists and the rules for working within it. Read this before
adding a feature or "fixing" something that looks odd — several things that look
like smells are deliberate, documented below under **Deferred / do not touch blindly**.
---
## 1. Layering
```
Presentation src/Screens/**, src/components/** (UI only)
│
Application src/Services/**, feature hooks (use-cases, orchestration)
│
Domain slices, reducers, pattern configs (business rules, pure)
│
Infrastructure src/core/** (http, storage, crypto, events)
│
Platform react-native, native modules
```
Dependencies point **inward**. `src/core/**` must never import from `Screens`,
`Services`, or `Redux`. It reaches outward only through injection/events
(see §3).
---
## 2. The `core/` layer — the single source of infrastructure truth
| Module | Responsibility | Rule |
|--------|----------------|------|
| `core/config/env.ts` | All hosts/base URLs, selected by build type | **No hardcoded URLs anywhere else.** |
| `core/http/client.ts` | THE axios instance; `request<T>()` | Throws `AppError`; never returns a sentinel. All new network code uses this. |
| `core/http/AppError.ts` | Normalized typed error | Catch with `isAppError()`; branch on `.kind`. |
| `core/observability/logger.ts` | The only sanctioned log sink | **Never `console.*` in app code.** DEV-only; release-stripped. |
| `core/observability/redact.ts` | PII/secret masking | Every log payload is redacted; extend `SENSITIVE_KEY_PATTERN` when adding sensitive fields. |
| `core/storage/secureStorage.ts` | Keychain-backed persist storage for the auth session | Token is never in plaintext AsyncStorage. |
| `core/events/appEvents.ts` | Typed cross-layer event bus | How infra signals up without importing the store. |
| `core/query/queryClient.ts` | Module-singleton React Query client | Never construct a QueryClient in a component. |
| `core/errors/ErrorBoundary.tsx` | App-root render-error guard | Wraps the tree in `App.tsx`. |
| `core/platform/polyfills.ts` | Buffer / TextEncoder | Imported first in `index.js`; nothing may depend on load order otherwise. |
---
## 3. Hard rules (enforced by review; some by CI)
1. **Networking:** use `request()` from `core/http/client`. It injects the JWT,
handles 401 (emits `auth/unauthorized`), times out, and throws `AppError`.
`src/Axios/Axios.ts` (`apiCall`) is a **legacy compatibility adapter** that
preserves the old never-throws `{success:false}` envelope for un-migrated
call sites; its contract is pinned by tests. Do not add new `apiCall` sites.
2. **Auth token:** read only via `selectAuthJwt(state)`. The transport reads it
through a provider registered in `Redux/Store.ts` — never `import { store }`
into `core`.
3. **Logout:** call `performLogout()` from `Services/Auth/session.ts`. It clears
redux + keychain + the query cache in one place. `AuthSessionGuard` (mounted
in `App.tsx`) resets navigation to Login whenever the session ends.
4. **Post-auth routing:** `Services/Auth/registrationRouting.ts` is the single
decision function. Splash and Login both call it. Do not re-implement.
5. **Logging:** `logger.<level>(scope, message, data?)`. Never log key material,
plaintext bids, OTPs, or full PII (the redactor is a safety net, not a licence).
6. **Theme/colors:** use `useTheme()`. `Constant/Color.tsx` is **deprecated**
(see §6). There is one brand orange, sourced from `theme/colors`.
7. **Components:** one root — `src/components/**`. `src/Component/` no longer exists.
8. **Navigation:** route param shapes live in `src/Navigation/types.ts`
(`RootStackParamList`). `NavigationString` is the `as const` route-name registry.
9. **Forward/Reverse bidding:** every behavioural difference lives in
`Screens/Bidding/biddingPattern.ts`. The screen/card contain zero
`if (pattern === ...)`.
---
## 4. Feature anatomy (pattern to copy)
The tender-bidding and registration features show the intended shape — logic is
extracted out of the screen into pure, testable units:
```
Screens/Tender/tenderBidding/
TenderBidding.tsx presentation + state only
envelopeTypes.ts types
envelopeCrypto.ts AES/RSA (never logs key material)
envelopePayload.ts pure payload build + validation ← unit-tested
envelopeSubmission.ts API calls
components/* memoized, module-scope sub-components
Screens/Ragistration/
Ragistration.tsx orchestration + render
registrationState.ts typed useReducer (was ~20 useStates) ← unit-tested
registrationValidation.ts pure per-stage validation ← unit-tested
```
When you build a feature: keep the screen thin; put decisions in pure functions;
test the pure functions.
---
## 5. Testing & CI
- `npm test` (jest). 75 tests across 11 suites, concentrated on the
money/crypto/auth/storage paths: `envelopePayload`, `biddingPattern`,
`registrationState`/`registrationValidation`, `registrationRouting`,
`Token` slice, `apiCall` envelope contract, `AppError`, `redact`, `appEvents`,
and the `secureStorage` keychain-migration path.
- CI (`.github/workflows/ci.yml`) runs **install → typecheck → lint → test**,
all **blocking**. `tsc --noEmit` is green; `eslint . --quiet` (errors) is green.
Style *warnings* still exist and are being burned down; tighten to
`--max-warnings=0` once clean.
- New pure logic should ship with tests. UI can follow with RNTL later.
---
## 6. Deferred / do not touch blindly
These look like leftovers but were left deliberately. Read before "fixing".
- **`Constant/Color.tsx`** is deprecated but still imported by ~20 files for
neutral grays. Its brand tokens are now sourced from the theme, but its neutral
values differ from `theme/colors` in several places. A blind find/replace to
`useTheme()` **will change appearance** — migrate screen-by-screen with visual
review.
- **`EnvelopeCExcelPreview.tsx`** still uses raw axios (`responseType:
'arraybuffer'`) and `react-native-fs` with `'ascii'` write encoding. The RNFS →
`react-native-blob-util` dedupe and the axios → `core/http` migration are
deferred because this sealed-bid document flow can't be runtime-verified here
and the encodings aren't 1:1. Needs a device.
- **Sealed-bid crypto** is AES-ECB with the symmetric key echoed to the server
(`randomKeyGenerated`). This is a backend-contract weakness (audit §11); fixing
it requires a coordinated backend change, not a client edit.
- **`ResetPassword`** sends the new password as a URL query param. Migrated to
`core/http` (so it's redacted in logs) but the query-param placement is a
backend contract — see the `TODO(backend)` at the call site.
- **`PurchaseTenderTile` "details" tap** navigates to `'TenderDetail'`, an
unregistered route (a pre-existing latent bug). The intended destination is
ambiguous (the tile already renders an in-tile modal), so it awaits a product
decision rather than a guessed target.
---
## 7. Open work (gated on device or backend/ops)
- **Device/emulator needed:** full Auth/App two-stack navigator split
(`AuthSessionGuard` + `registrationRouting` are the groundwork); release
minify/shrink + App Bundle + ABI splits; verifying the Envelope C flow.
- **Backend/ops needed:** HTTPS for `eproc-backend.lincpay` (then remove its
cleartext exemption in `network_security_config.xml` and add pinning); rotate
the previously-committed keystore + Sonar token and purge git history.
- **Incremental:** finish `Constant/Color` → `useTheme`; per-screen typed
navigation adoption (`useNavigation<NativeStackNavigationProp<RootStackParamList>>`);
broaden accessibility labels; i18n scaffolding.