kata-context 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,601 @@
1
+ # Architecture Patterns
2
+
3
+ **Project:** Kata Context - Context Policy Engine
4
+ **Domain:** REST API with TypeScript/Python SDKs
5
+ **Researched:** 2026-01-29
6
+
7
+ ## Executive Summary
8
+
9
+ Based on research into Vercel serverless architecture, TypeScript project organization, and multi-language SDK management, I recommend:
10
+
11
+ 1. **Monorepo structure** using Turborepo + pnpm workspaces
12
+ 2. **Pure serverless API** using Vercel Functions (not Next.js)
13
+ 3. **Feature-first organization** within the API package
14
+ 4. **Separate SDK packages** co-located in the monorepo
15
+
16
+ **Confidence:** HIGH for Vercel patterns, MEDIUM for SDK organization (based on industry examples)
17
+
18
+ ---
19
+
20
+ ## Recommended Architecture
21
+
22
+ ### Decision: Monorepo (Not Polyrepo)
23
+
24
+ **Recommendation:** Monorepo with Turborepo + pnpm workspaces
25
+
26
+ **Rationale:**
27
+ - Single repository enables coordinated releases between API and SDKs
28
+ - Shared TypeScript types between API and TypeScript SDK
29
+ - Unified CI/CD pipeline
30
+ - AI tooling benefits from full context visibility
31
+ - Apache 2.0 licensing applies uniformly
32
+
33
+ **Trade-off accepted:** Python SDK will need its own tooling (Poetry/pip) within the monorepo, but Turborepo can orchestrate Python builds.
34
+
35
+ **Sources:**
36
+ - [Turborepo Repository Structure](https://turborepo.dev/docs/crafting-your-repository/structuring-a-repository)
37
+ - [Monorepo vs Polyrepo Analysis](https://www.aviator.co/blog/monorepo-vs-polyrepo/)
38
+
39
+ ### Decision: Pure Vercel Functions (Not Next.js)
40
+
41
+ **Recommendation:** Use Vercel Functions directly with the `/api` directory pattern
42
+
43
+ **Rationale:**
44
+ - Kata Context is a REST API, not a web application
45
+ - No need for React, SSR, or frontend routing
46
+ - Zero-configuration deployment with `/api` directory
47
+ - Lower complexity and smaller bundle sizes
48
+ - Direct access to Vercel's Fluid Compute features
49
+
50
+ **What we avoid:**
51
+ - Next.js App Router vs Pages Router complexity
52
+ - Unnecessary React Server Components
53
+ - Frontend build overhead
54
+
55
+ **Sources:**
56
+ - [Vercel Functions Documentation](https://vercel.com/docs/functions)
57
+ - [Hosting Backend APIs on Vercel](https://vercel.com/kb/guide/hosting-backend-apis)
58
+
59
+ ---
60
+
61
+ ## Directory Structure
62
+
63
+ ### Complete Monorepo Layout
64
+
65
+ ```
66
+ kata-context/
67
+ ├── .github/
68
+ │ └── workflows/
69
+ │ ├── ci.yml # Unified CI pipeline
70
+ │ ├── release-api.yml # API deployment
71
+ │ ├── release-sdk-ts.yml # npm publish
72
+ │ └── release-sdk-python.yml # PyPI publish
73
+
74
+ ├── .planning/ # Kata planning artifacts
75
+ │ ├── PROJECT.md
76
+ │ ├── ROADMAP.md
77
+ │ └── research/
78
+
79
+ ├── apps/
80
+ │ └── api/ # Vercel serverless API
81
+ │ ├── api/ # Vercel Functions (route handlers)
82
+ │ │ ├── v1/
83
+ │ │ │ ├── policies/
84
+ │ │ │ │ ├── index.ts # GET /api/v1/policies
85
+ │ │ │ │ ├── [id].ts # GET/PUT/DELETE /api/v1/policies/:id
86
+ │ │ │ │ └── evaluate.ts # POST /api/v1/policies/evaluate
87
+ │ │ │ ├── contexts/
88
+ │ │ │ │ ├── index.ts
89
+ │ │ │ │ └── [id].ts
90
+ │ │ │ └── health.ts # GET /api/v1/health
91
+ │ │ └── health.ts # GET /api/health (root health check)
92
+ │ │
93
+ │ ├── src/ # Application source code
94
+ │ │ ├── domain/ # Core business logic
95
+ │ │ │ ├── policy/
96
+ │ │ │ │ ├── policy.ts
97
+ │ │ │ │ ├── policy.service.ts
98
+ │ │ │ │ └── policy.repository.ts
99
+ │ │ │ └── context/
100
+ │ │ │ ├── context.ts
101
+ │ │ │ ├── context.service.ts
102
+ │ │ │ └── context.repository.ts
103
+ │ │ │
104
+ │ │ ├── infrastructure/ # External integrations
105
+ │ │ │ ├── database/
106
+ │ │ │ │ └── client.ts
107
+ │ │ │ └── cache/
108
+ │ │ │ └── client.ts
109
+ │ │ │
110
+ │ │ ├── shared/ # Cross-cutting concerns
111
+ │ │ │ ├── errors/
112
+ │ │ │ │ └── api-error.ts
113
+ │ │ │ ├── middleware/
114
+ │ │ │ │ ├── auth.ts
115
+ │ │ │ │ └── validation.ts
116
+ │ │ │ └── utils/
117
+ │ │ │ └── response.ts
118
+ │ │ │
119
+ │ │ └── types/ # TypeScript type definitions
120
+ │ │ └── index.ts
121
+ │ │
122
+ │ ├── package.json
123
+ │ ├── tsconfig.json
124
+ │ └── vercel.ts # Vercel configuration
125
+
126
+ ├── packages/
127
+ │ ├── typescript-sdk/ # @kata/context TypeScript SDK
128
+ │ │ ├── src/
129
+ │ │ │ ├── index.ts
130
+ │ │ │ ├── client.ts
131
+ │ │ │ ├── types.ts
132
+ │ │ │ └── errors.ts
133
+ │ │ ├── package.json
134
+ │ │ ├── tsconfig.json
135
+ │ │ └── README.md
136
+ │ │
137
+ │ ├── python-sdk/ # kata-context Python SDK
138
+ │ │ ├── kata_context/
139
+ │ │ │ ├── __init__.py
140
+ │ │ │ ├── client.py
141
+ │ │ │ ├── types.py
142
+ │ │ │ └── errors.py
143
+ │ │ ├── tests/
144
+ │ │ ├── pyproject.toml
145
+ │ │ └── README.md
146
+ │ │
147
+ │ └── shared-types/ # Shared TypeScript types (optional)
148
+ │ ├── src/
149
+ │ │ └── index.ts
150
+ │ ├── package.json
151
+ │ └── tsconfig.json
152
+
153
+ ├── turbo.json # Turborepo configuration
154
+ ├── pnpm-workspace.yaml # pnpm workspace definition
155
+ ├── package.json # Root package.json
156
+ ├── tsconfig.base.json # Shared TypeScript config
157
+ ├── .eslintrc.js # Shared ESLint config
158
+ ├── .prettierrc # Shared Prettier config
159
+ └── README.md
160
+ ```
161
+
162
+ ### Key Structural Decisions
163
+
164
+ #### 1. API Routes in `/api` Directory
165
+
166
+ Vercel automatically creates serverless functions from files in the `/api` directory. Each file becomes an endpoint:
167
+
168
+ ```
169
+ api/v1/policies/index.ts -> GET/POST /api/v1/policies
170
+ api/v1/policies/[id].ts -> GET/PUT/DELETE /api/v1/policies/:id
171
+ api/v1/policies/evaluate.ts -> POST /api/v1/policies/evaluate
172
+ ```
173
+
174
+ **Why this pattern:**
175
+ - Zero-configuration routing
176
+ - File-based routing is explicit and discoverable
177
+ - Dynamic routes via `[param].ts` syntax
178
+ - Vercel handles function bundling automatically
179
+
180
+ #### 2. Feature-First Domain Organization
181
+
182
+ Within `/src/domain/`, organize by business domain, not by technical layer:
183
+
184
+ ```
185
+ domain/
186
+ ├── policy/ # Everything about policies
187
+ │ ├── policy.ts # Domain entity
188
+ │ ├── policy.service.ts # Business logic
189
+ │ └── policy.repository.ts # Data access
190
+ └── context/ # Everything about contexts
191
+ ├── context.ts
192
+ ├── context.service.ts
193
+ └── context.repository.ts
194
+ ```
195
+
196
+ **Why this pattern:**
197
+ - Co-locates related code
198
+ - Scales better than layer-first (controllers/, services/, models/)
199
+ - Easier to reason about a single feature
200
+ - Supports future microservice extraction if needed
201
+
202
+ #### 3. SDK Package Naming
203
+
204
+ | Package | npm/PyPI Name | Import |
205
+ |---------|---------------|--------|
206
+ | TypeScript SDK | `@kata/context` | `import { KataContext } from '@kata/context'` |
207
+ | Python SDK | `kata-context` | `from kata_context import KataContext` |
208
+
209
+ ---
210
+
211
+ ## Configuration Files
212
+
213
+ ### Root package.json
214
+
215
+ ```json
216
+ {
217
+ "name": "kata-context",
218
+ "private": true,
219
+ "scripts": {
220
+ "build": "turbo run build",
221
+ "dev": "turbo run dev",
222
+ "lint": "turbo run lint",
223
+ "test": "turbo run test",
224
+ "format": "prettier --write \"**/*.{ts,tsx,md}\""
225
+ },
226
+ "devDependencies": {
227
+ "turbo": "^2.x",
228
+ "prettier": "^3.x",
229
+ "eslint": "^9.x",
230
+ "typescript": "^5.x"
231
+ },
232
+ "packageManager": "pnpm@9.x"
233
+ }
234
+ ```
235
+
236
+ ### pnpm-workspace.yaml
237
+
238
+ ```yaml
239
+ packages:
240
+ - "apps/*"
241
+ - "packages/*"
242
+ ```
243
+
244
+ ### turbo.json
245
+
246
+ ```json
247
+ {
248
+ "$schema": "https://turbo.build/schema.json",
249
+ "tasks": {
250
+ "build": {
251
+ "dependsOn": ["^build"],
252
+ "outputs": ["dist/**", ".next/**", ".vercel/**"]
253
+ },
254
+ "dev": {
255
+ "cache": false,
256
+ "persistent": true
257
+ },
258
+ "lint": {},
259
+ "test": {
260
+ "dependsOn": ["build"]
261
+ }
262
+ }
263
+ }
264
+ ```
265
+
266
+ ### apps/api/vercel.ts
267
+
268
+ ```typescript
269
+ import { routes, type VercelConfig } from '@vercel/config/v1';
270
+
271
+ export const config: VercelConfig = {
272
+ // Enable Fluid Compute for better concurrency
273
+ fluid: true,
274
+
275
+ // Clean URLs (no .ts/.js extensions)
276
+ cleanUrls: true,
277
+
278
+ // Trailing slash handling
279
+ trailingSlash: false,
280
+
281
+ // API versioning via rewrites (optional, for future versions)
282
+ rewrites: [
283
+ // Latest version alias
284
+ routes.rewrite('/api/policies/(.*)', '/api/v1/policies/$1'),
285
+ routes.rewrite('/api/contexts/(.*)', '/api/v1/contexts/$1'),
286
+ ],
287
+
288
+ // CORS headers for SDK access
289
+ headers: [
290
+ routes.header('/api/(.*)', [
291
+ { key: 'Access-Control-Allow-Origin', value: '*' },
292
+ { key: 'Access-Control-Allow-Methods', value: 'GET, POST, PUT, DELETE, OPTIONS' },
293
+ { key: 'Access-Control-Allow-Headers', value: 'Content-Type, Authorization' },
294
+ ]),
295
+ ],
296
+
297
+ // Function configuration
298
+ functions: {
299
+ 'api/**/*.ts': {
300
+ maxDuration: 30,
301
+ },
302
+ },
303
+ };
304
+ ```
305
+
306
+ ### apps/api/tsconfig.json
307
+
308
+ ```json
309
+ {
310
+ "extends": "../../tsconfig.base.json",
311
+ "compilerOptions": {
312
+ "target": "ES2022",
313
+ "module": "ESNext",
314
+ "moduleResolution": "bundler",
315
+ "strict": true,
316
+ "esModuleInterop": true,
317
+ "skipLibCheck": true,
318
+ "forceConsistentCasingInFileNames": true,
319
+ "outDir": "dist",
320
+ "rootDir": ".",
321
+ "baseUrl": ".",
322
+ "paths": {
323
+ "@/*": ["src/*"]
324
+ }
325
+ },
326
+ "include": ["src/**/*", "api/**/*"],
327
+ "exclude": ["node_modules", "dist"]
328
+ }
329
+ ```
330
+
331
+ ---
332
+
333
+ ## Component Boundaries
334
+
335
+ ### API Layer Responsibilities
336
+
337
+ | Component | Responsibility | Communicates With |
338
+ |-----------|----------------|-------------------|
339
+ | Route Handlers (`api/`) | HTTP request/response, validation | Domain Services |
340
+ | Domain Services (`src/domain/*/service.ts`) | Business logic, orchestration | Repositories, other Services |
341
+ | Repositories (`src/domain/*/repository.ts`) | Data access abstraction | Infrastructure (Database) |
342
+ | Infrastructure (`src/infrastructure/`) | External service clients | External APIs, databases |
343
+
344
+ ### Data Flow
345
+
346
+ ```
347
+ Request → Route Handler → Validation → Service → Repository → Database
348
+
349
+ Response ← Route Handler ← Service ← Domain Entity
350
+ ```
351
+
352
+ ### SDK Layer Responsibilities
353
+
354
+ | Component | Responsibility |
355
+ |-----------|----------------|
356
+ | Client | HTTP client wrapper, authentication |
357
+ | Types | TypeScript/Python type definitions matching API |
358
+ | Errors | Domain-specific error handling |
359
+
360
+ ---
361
+
362
+ ## Patterns to Follow
363
+
364
+ ### Pattern 1: Vercel Function Handler
365
+
366
+ **What:** Standard pattern for Vercel serverless functions with typed request/response.
367
+
368
+ **When:** Every API route handler.
369
+
370
+ **Example:**
371
+
372
+ ```typescript
373
+ // api/v1/policies/[id].ts
374
+ import type { VercelRequest, VercelResponse } from '@vercel/node';
375
+ import { PolicyService } from '@/domain/policy/policy.service';
376
+ import { ApiError } from '@/shared/errors/api-error';
377
+
378
+ export default async function handler(
379
+ request: VercelRequest,
380
+ response: VercelResponse
381
+ ) {
382
+ const { id } = request.query;
383
+
384
+ if (typeof id !== 'string') {
385
+ return response.status(400).json({ error: 'Invalid policy ID' });
386
+ }
387
+
388
+ try {
389
+ switch (request.method) {
390
+ case 'GET':
391
+ const policy = await PolicyService.getById(id);
392
+ if (!policy) {
393
+ return response.status(404).json({ error: 'Policy not found' });
394
+ }
395
+ return response.status(200).json(policy);
396
+
397
+ case 'PUT':
398
+ const updated = await PolicyService.update(id, request.body);
399
+ return response.status(200).json(updated);
400
+
401
+ case 'DELETE':
402
+ await PolicyService.delete(id);
403
+ return response.status(204).end();
404
+
405
+ default:
406
+ response.setHeader('Allow', ['GET', 'PUT', 'DELETE']);
407
+ return response.status(405).json({ error: 'Method not allowed' });
408
+ }
409
+ } catch (error) {
410
+ if (error instanceof ApiError) {
411
+ return response.status(error.statusCode).json({ error: error.message });
412
+ }
413
+ console.error('Unexpected error:', error);
414
+ return response.status(500).json({ error: 'Internal server error' });
415
+ }
416
+ }
417
+ ```
418
+
419
+ ### Pattern 2: Service Layer Abstraction
420
+
421
+ **What:** Business logic isolated from HTTP concerns.
422
+
423
+ **When:** Any non-trivial business logic.
424
+
425
+ **Example:**
426
+
427
+ ```typescript
428
+ // src/domain/policy/policy.service.ts
429
+ import { PolicyRepository } from './policy.repository';
430
+ import type { Policy, CreatePolicyInput, UpdatePolicyInput } from './policy';
431
+
432
+ export class PolicyService {
433
+ static async getById(id: string): Promise<Policy | null> {
434
+ return PolicyRepository.findById(id);
435
+ }
436
+
437
+ static async create(input: CreatePolicyInput): Promise<Policy> {
438
+ // Business validation
439
+ this.validatePolicyRules(input.rules);
440
+
441
+ return PolicyRepository.create(input);
442
+ }
443
+
444
+ static async evaluate(policyId: string, context: Record<string, unknown>): Promise<boolean> {
445
+ const policy = await this.getById(policyId);
446
+ if (!policy) {
447
+ throw new NotFoundError('Policy not found');
448
+ }
449
+
450
+ // Core evaluation logic
451
+ return this.evaluateRules(policy.rules, context);
452
+ }
453
+
454
+ private static validatePolicyRules(rules: unknown): void {
455
+ // Validation logic
456
+ }
457
+
458
+ private static evaluateRules(rules: PolicyRule[], context: Record<string, unknown>): boolean {
459
+ // Evaluation logic
460
+ }
461
+ }
462
+ ```
463
+
464
+ ### Pattern 3: Shared Types Between API and TypeScript SDK
465
+
466
+ **What:** Single source of truth for TypeScript types.
467
+
468
+ **When:** API response shapes, request bodies.
469
+
470
+ **Example:**
471
+
472
+ ```typescript
473
+ // packages/shared-types/src/index.ts
474
+ export interface Policy {
475
+ id: string;
476
+ name: string;
477
+ description?: string;
478
+ rules: PolicyRule[];
479
+ createdAt: string;
480
+ updatedAt: string;
481
+ }
482
+
483
+ export interface PolicyRule {
484
+ field: string;
485
+ operator: 'equals' | 'contains' | 'gt' | 'lt' | 'in';
486
+ value: unknown;
487
+ }
488
+
489
+ export interface EvaluationResult {
490
+ allowed: boolean;
491
+ policyId: string;
492
+ matchedRules: string[];
493
+ evaluatedAt: string;
494
+ }
495
+
496
+ export interface ApiError {
497
+ error: string;
498
+ code?: string;
499
+ details?: Record<string, unknown>;
500
+ }
501
+ ```
502
+
503
+ ---
504
+
505
+ ## Anti-Patterns to Avoid
506
+
507
+ ### Anti-Pattern 1: Fat Route Handlers
508
+
509
+ **What:** Putting business logic directly in route handlers.
510
+
511
+ **Why bad:**
512
+ - Untestable without HTTP mocking
513
+ - Logic cannot be reused
514
+ - Handlers become unmaintainable
515
+
516
+ **Instead:**
517
+ - Route handlers only handle HTTP concerns
518
+ - Delegate to service layer for business logic
519
+ - Services are pure functions, easily testable
520
+
521
+ ### Anti-Pattern 2: Layer-First Organization
522
+
523
+ **What:** Organizing by technical layer (`controllers/`, `services/`, `models/`).
524
+
525
+ **Why bad:**
526
+ - Related code scattered across directories
527
+ - Adding a feature touches many directories
528
+ - Harder to understand feature boundaries
529
+
530
+ **Instead:**
531
+ - Feature-first organization (`policy/`, `context/`)
532
+ - Each feature contains its controller, service, repository
533
+ - Cross-cutting concerns in `shared/`
534
+
535
+ ### Anti-Pattern 3: Next.js for Pure APIs
536
+
537
+ **What:** Using Next.js when you only need an API.
538
+
539
+ **Why bad:**
540
+ - Unnecessary React/frontend overhead
541
+ - App Router vs Pages Router complexity
542
+ - Larger bundle sizes
543
+ - Build time overhead
544
+
545
+ **Instead:**
546
+ - Pure Vercel Functions with `/api` directory
547
+ - Only add Next.js if you need a frontend
548
+
549
+ ### Anti-Pattern 4: Polyrepo for Tightly Coupled Components
550
+
551
+ **What:** Separate repositories for API and SDKs when they share types/releases.
552
+
553
+ **Why bad:**
554
+ - Version coordination complexity
555
+ - Type drift between API and SDKs
556
+ - Multiple CI/CD pipelines to maintain
557
+ - Harder for contributors
558
+
559
+ **Instead:**
560
+ - Monorepo with Turborepo
561
+ - Shared types package
562
+ - Coordinated releases
563
+
564
+ ---
565
+
566
+ ## Scalability Considerations
567
+
568
+ | Concern | At 100 users | At 10K users | At 1M users |
569
+ |---------|--------------|--------------|-------------|
570
+ | **Request handling** | Single region Vercel Functions | Multi-region deployment | Multi-region + edge caching |
571
+ | **Database** | Serverless DB (Turso, Neon) | Serverless DB with read replicas | Dedicated database + caching layer |
572
+ | **Caching** | None needed | Vercel Edge Cache | Redis/Upstash + Edge Cache |
573
+ | **Rate limiting** | Basic (per-IP) | API key based | Token bucket with Redis |
574
+ | **SDK distribution** | npm/PyPI | npm/PyPI + CDN | npm/PyPI + CDN + enterprise support |
575
+
576
+ ### Vercel Function Limits to Consider
577
+
578
+ | Limit | Hobby | Pro | Enterprise |
579
+ |-------|-------|-----|------------|
580
+ | Duration | 10s | 60s (default 15s) | 900s (default 15s) |
581
+ | Memory | 1024MB | Configurable | Configurable |
582
+ | Payload | 4.5MB | 4.5MB | 4.5MB |
583
+
584
+ **Source:** [Vercel Functions Documentation](https://vercel.com/docs/functions)
585
+
586
+ ---
587
+
588
+ ## Sources
589
+
590
+ ### HIGH Confidence (Official Documentation)
591
+ - [Vercel Functions](https://vercel.com/docs/functions) - Serverless function patterns
592
+ - [Vercel vercel.ts Configuration](https://vercel.com/docs/project-configuration/vercel-ts) - Programmatic configuration
593
+ - [Turborepo Repository Structure](https://turborepo.dev/docs/crafting-your-repository/structuring-a-repository) - Monorepo patterns
594
+
595
+ ### MEDIUM Confidence (Verified Patterns)
596
+ - [Hosting Backend APIs on Vercel](https://vercel.com/kb/guide/hosting-backend-apis) - Pure API deployment
597
+ - [Monorepo Tools Comparison](https://monorepo.tools/) - Tool ecosystem overview
598
+
599
+ ### LOW Confidence (Community Patterns)
600
+ - [Feature-First Organization](https://dev.to/pramod_boda/recommended-folder-structure-for-nodets-2025-39jl) - Modern TypeScript organization
601
+ - [Multi-Language Monorepo Examples](https://github.com/palmerhq/monorepo-starter) - Polyglot monorepo patterns