test-simple-calculator-docs/architecture/tad.md

117 lines
5.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Tech Architecture Document
## Stack
| Layer | Choice | Rationale |
|---|---|---|
| Language | TypeScript | Type safety for arithmetic logic; catches operand/type bugs at compile time. |
| UI Framework | React 18 (Vite) | Lightweight component model; Vite gives fast dev HMR and a zero-config static build output. |
| Styling | CSS Modules | Scoped styles, no runtime overhead, easy responsive layout. |
| Testing | Vitest + React Testing Library | Co-located with Vite; unit tests for arithmetic logic, component interaction tests for UI. |
| Build / Deploy | Vite `build` → static files | Produces a plain `dist/` folder deployable to any static host (Netlify, GitHub Pages, S3, etc.). |
| CI | GitHub Actions | Lint, type-check, test, and build on every push; deploy `dist/` on merge to `main`. |
---
## Components
```mermaid
graph TD
App["App (root)"]
Display["Display\n• shows current operand A, operator, operand B\n• shows result or error"]
OperandInput["OperandInput × 2\n• controlled numeric text field\n• accepts decimal & negative values"]
OperatorSelector["OperatorSelector\n• four buttons: + × ÷\n• highlights active operator"]
ActionBar["ActionBar\n• = (Calculate) button\n• C (Clear) button"]
Calculator["calculator.ts\n(pure logic module)"]
App --> Display
App --> OperandInput
App --> OperatorSelector
App --> ActionBar
App -- "calls" --> Calculator
```
### Component responsibilities
| Component | Responsibility |
|---|---|
| `App` | Owns all state (operandA, operandB, operator, result, error). Wires child events to state updates and calls the calculator module. |
| `Display` | Pure presentational; renders the current expression and result/error string. |
| `OperandInput` | Controlled `<input type="text">` that accepts numeric strings including decimals; reports `onChange` to `App`. |
| `OperatorSelector` | Renders four operator buttons; reports `onSelect` to `App`. |
| `ActionBar` | Renders **=** and **C** buttons; fires `onCalculate` / `onClear` callbacks. |
| `calculator.ts` | Pure-function module: `calculate(a: number, op: Operator, b: number): number \| CalcError`. No DOM dependency; fully unit-testable. |
---
## Data Model
All state is ephemeral (in-memory React state). There is no database, localStorage, or network persistence.
```mermaid
erDiagram
APP_STATE {
string operandA "raw input string for operand A"
string operandB "raw input string for operand B"
Operator operator "'+' | '-' | '*' | '/'"
number result "computed result (null until calculated)"
string errorMessage "non-null when a calc error occurs"
}
```
### Type definitions (TypeScript)
```ts
type Operator = '+' | '-' | '*' | '/';
type CalcError =
| { kind: 'DIVISION_BY_ZERO' }
| { kind: 'INVALID_INPUT'; field: 'a' | 'b' };
type CalcResult = { ok: true; value: number } | { ok: false; error: CalcError };
```
`calculator.ts` exports a single function:
```ts
export function calculate(a: number, op: Operator, b: number): CalcResult;
```
---
## Integrations
This version has **no external integrations**. All computation runs entirely in the browser.
| Integration | Status | Notes |
|---|---|---|
| Backend API | None | All arithmetic is client-side; NFR-6 (static deployability) rules out a server runtime. |
| Authentication service | None | No accounts or sessions (Assumption 2). |
| Analytics / telemetry | None | Out of scope for v1; can be added later without architectural change. |
| CDN / static host | Optional | Any static host works (`dist/` is self-contained). Recommended: Netlify or GitHub Pages for zero-cost deployment. |
---
## Non-Functional Requirements
| ID | NFR | Architectural approach |
|---|---|---|
| NFR-1 | Result ≤ 100 ms | All logic runs synchronously in the browser; no network round-trip. Vite-bundled output is ≤ 50 kB gzipped. |
| NFR-2 | Responsive (desktop + mobile) | CSS Modules with a fluid grid; `OperandInput` and `OperatorSelector` reflow to single-column below 480 px. |
| NFR-3 | WCAG 2.1 AA | Semantic HTML (`<button>`, `<label>`, `<input>`); colour contrast validated in design tokens; all interactive elements reachable via Tab; `aria-live` region for result/error announcements. |
| NFR-4 | Chrome / Firefox / Safari / Edge | TypeScript compiled to ES2020 (broadly supported); no experimental browser APIs used. Vitest browser-mode smoke test on CI. |
| NFR-5 | Modular + unit-tested | `calculator.ts` is a pure module with 100 % branch coverage required in CI gate. |
| NFR-6 | Static deployable | Vite `build` emits plain HTML/CSS/JS to `dist/`; no Node.js runtime needed at serve time. |
---
## Risks
| # | Risk | Likelihood | Impact | Mitigation |
|---|---|---|---|---|
| R-1 | Floating-point display artefacts (e.g. `0.1 + 0.2 = 0.30000000000000004`) | High | Low | Round display output to a configurable number of significant figures (default 10); document IEEE 754 behaviour in the UI tooltip. |
| R-2 | Invalid / non-numeric operand input | Medium | Medium | Parse operands before passing to `calculate()`; show inline validation error via `CalcError.INVALID_INPUT` rather than `NaN` propagation. |
| R-3 | Division-by-zero unhandled path | Low | Medium | `calculate()` returns `CalcError.DIVISION_BY_ZERO` as a typed value; `App` maps this to a user-readable message. Unit-tested. |
| R-4 | Accessibility regressions on future style changes | Low | Medium | Axe-core accessibility checks run in CI on every PR via `@axe-core/react` dev integration. |
| R-5 | Scope creep (expression parser, history, etc.) | Medium | Medium | Documented out-of-scope items in Working Spec Assumptions 2, 3, 6, 7; change requests require a new spec revision. |