docs(architecture): sync working spec + TAD from AI-SDLC (Gate 2)
This commit is contained in:
parent
0d1319dccc
commit
0ff56da463
147
architecture/tad.md
Normal file
147
architecture/tad.md
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
# Tech Architecture Document
|
||||||
|
|
||||||
|
## Stack
|
||||||
|
|
||||||
|
| Layer | Technology | Version / Notes |
|
||||||
|
|---|---|---|
|
||||||
|
| Runtime | Node.js | LTS (≥ 20.x) |
|
||||||
|
| Language | TypeScript | ≥ 5.x, compiled to CommonJS or ESM for execution |
|
||||||
|
| Framework | Express.js | ≥ 4.x |
|
||||||
|
| Test runner | Jest or Vitest | Unit tests for counting logic |
|
||||||
|
| Build tooling | `tsc` (TypeScript compiler) | No bundler required |
|
||||||
|
| Package manager | npm | Standard `npm install && npm start` workflow |
|
||||||
|
|
||||||
|
## Components
|
||||||
|
|
||||||
|
### 1. HTTP Server (`src/server.ts`)
|
||||||
|
Bootstraps the Express application, registers middleware (JSON body-parser, error handler), mounts routes, and starts listening on `process.env.PORT` (default `3000`).
|
||||||
|
|
||||||
|
### 2. Router (`src/routes/count.ts`, `src/routes/health.ts`)
|
||||||
|
Declares the two route handlers:
|
||||||
|
- **POST /count** — validates the request body, calls the counting service, and returns the result.
|
||||||
|
- **GET /health** — returns `{ "status": "ok" }` with HTTP 200.
|
||||||
|
|
||||||
|
### 3. Counting Service (`src/services/counter.ts`)
|
||||||
|
Pure functions with no side-effects:
|
||||||
|
- `countWords(text: string): number` — trims the input, splits on `/\s+/`, filters empty tokens, returns the token count.
|
||||||
|
- `countCharacters(text: string): number` — returns `text.length` (UTF-16 code units, spaces included).
|
||||||
|
|
||||||
|
### 4. Validation Middleware (`src/middleware/validateCount.ts`)
|
||||||
|
Express middleware applied to `POST /count` that checks:
|
||||||
|
1. Body is present and `Content-Type: application/json` was parsed successfully.
|
||||||
|
2. `text` field exists.
|
||||||
|
3. `text` is of type `string`.
|
||||||
|
Returns HTTP 400 with a descriptive `{ "error": string }` payload on any violation.
|
||||||
|
|
||||||
|
### 5. Error Handler (`src/middleware/errorHandler.ts`)
|
||||||
|
Global Express error-handling middleware (four-argument signature) that catches any unhandled errors — including JSON parse errors emitted by `express.json()` — and returns a 400 response with a descriptive message.
|
||||||
|
|
||||||
|
### 6. Unit Tests (`src/__tests__/counter.test.ts`)
|
||||||
|
Tests for `countWords` and `countCharacters` covering: normal input, multiple/consecutive whitespace, leading/trailing whitespace, empty string, and character count including spaces.
|
||||||
|
|
||||||
|
## Data Model
|
||||||
|
|
||||||
|
No persistence layer exists. All data is transient per request.
|
||||||
|
|
||||||
|
### Request — `POST /count`
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"text": "hello world"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Response — `POST /count` (HTTP 200)
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"words": 2,
|
||||||
|
"characters": 11
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Response — `GET /health` (HTTP 200)
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"status": "ok"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Error Response (HTTP 400)
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"error": "<descriptive message>"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Integrations
|
||||||
|
|
||||||
|
None. The service is fully self-contained with no external APIs, databases, message queues, or third-party services.
|
||||||
|
|
||||||
|
## Architecture Diagram
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph TD
|
||||||
|
Client["HTTP Client\n(curl / test suite / browser)"]
|
||||||
|
|
||||||
|
subgraph "WCNT API (Node.js / Express)"
|
||||||
|
Server["Express App\nsrc/server.ts"]
|
||||||
|
HealthRoute["GET /health\nsrc/routes/health.ts"]
|
||||||
|
CountRoute["POST /count\nsrc/routes/count.ts"]
|
||||||
|
ValidateMW["Validation Middleware\nsrc/middleware/validateCount.ts"]
|
||||||
|
ErrMW["Error Handler\nsrc/middleware/errorHandler.ts"]
|
||||||
|
CounterSvc["Counting Service\nsrc/services/counter.ts\ncountWords() · countCharacters()"]
|
||||||
|
end
|
||||||
|
|
||||||
|
Client -->|HTTP request| Server
|
||||||
|
Server --> HealthRoute
|
||||||
|
Server --> ValidateMW
|
||||||
|
ValidateMW -->|valid| CountRoute
|
||||||
|
ValidateMW -->|invalid| ErrMW
|
||||||
|
CountRoute --> CounterSvc
|
||||||
|
CounterSvc -->|result| CountRoute
|
||||||
|
CountRoute -->|200 JSON| Client
|
||||||
|
HealthRoute -->|200 JSON| Client
|
||||||
|
ErrMW -->|400 JSON| Client
|
||||||
|
```
|
||||||
|
|
||||||
|
## Request / Response Data Flow
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant C as HTTP Client
|
||||||
|
participant E as Express App
|
||||||
|
participant V as Validation MW
|
||||||
|
participant R as /count Route
|
||||||
|
participant S as Counter Service
|
||||||
|
|
||||||
|
C->>E: POST /count { "text": "hello world" }
|
||||||
|
E->>V: parse JSON body
|
||||||
|
alt body invalid or text missing/non-string
|
||||||
|
V-->>C: HTTP 400 { "error": "..." }
|
||||||
|
else body valid
|
||||||
|
V->>R: next()
|
||||||
|
R->>S: countWords(text), countCharacters(text)
|
||||||
|
S-->>R: { words: 2, characters: 11 }
|
||||||
|
R-->>C: HTTP 200 { "words": 2, "characters": 11 }
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
## NFRs
|
||||||
|
|
||||||
|
| ID | Category | How Addressed |
|
||||||
|
|---|---|---|
|
||||||
|
| NFR-1 | Runtime | Node.js LTS; TypeScript source compiled via `tsc` before execution. |
|
||||||
|
| NFR-2 | Framework | Express.js; `express.json()` middleware for body parsing. |
|
||||||
|
| NFR-3 | Persistence | No database; all state is request-scoped and discarded after the response. |
|
||||||
|
| NFR-4 | Security | No auth layer; endpoint is open by design for demo use. |
|
||||||
|
| NFR-5 | Testing | Jest/Vitest unit tests for `countWords` and `countCharacters`; executed in CI via `npm test`. |
|
||||||
|
| NFR-6 | Simplicity | Single repository; zero non-Node dependencies beyond Express and the test runner. |
|
||||||
|
| NFR-7 | Portability | No native add-ons or OS-specific code; runs inside any standard `node:lts` Docker image or bare Node environment. |
|
||||||
|
|
||||||
|
## Risks
|
||||||
|
|
||||||
|
| ID | Risk | Likelihood | Impact | Mitigation |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| R-1 | Unicode surrogate pairs counted as 2 characters | Low | Low | Documented as Assumption A4; acceptable for demo scope. Upgrade to `Intl.Segmenter` if true grapheme count is ever required. |
|
||||||
|
| R-2 | Malformed `Content-Type` header bypasses JSON parsing | Low | Low | Global error handler catches `SyntaxError` from `express.json()` and returns 400. |
|
||||||
|
| R-3 | Port conflict on default `3000` | Medium | Low | `PORT` environment variable allows any port; documented in README / startup instructions. |
|
||||||
|
| R-4 | Dependency vulnerabilities in Express | Medium | Medium | Kept on latest stable Express 4.x; `npm audit` run in CI; minimal transitive dependency surface. |
|
||||||
Loading…
Reference in New Issue
Block a user