# Architecture Decision Records (ADR) This document records important architectural decisions made during ROA2WEB development. --- ## ADR-001: Ultrathin Monolith Architecture (2025-12-24) **Status:** Accepted **Context:** Originally designed as separate microservices (Reports, Data Entry, Telegram Bot as independent services). **Decision:** Consolidated into **ultrathin monolith** - single FastAPI application with modular structure. **Consequences:** - ✅ Single Windows service to manage - ✅ Shared database connection pool - ✅ Simpler deployment - ✅ Lower memory usage - ✅ Easier debugging - ⚠️ Requires careful module isolation **Implementation:** - All modules under `backend/modules/` - Single `ROA2WEB-Backend` service - Module enable/disable via `.env` flags --- ## ADR-002: Single Worker Configuration (2025-12-29) **Status:** Accepted **Context:** Telegram bot integration causes conflicts when multiple uvicorn workers run simultaneously. Each worker spawns its own bot instance, but Telegram API allows only ONE bot to poll for updates. **Problem:** ``` telegram.error.Conflict: terminated by other getUpdates request ``` **Decision:** **Always use `--workers 1` for the unified backend service.** **Rationale:** 1. **Telegram Bot Requirement:** Single bot instance only 2. **SQLite Cache:** Multiple workers cause locking conflicts 3. **Performance:** Async I/O means single worker handles 100+ concurrent requests 4. **Resources:** Lower memory (~400 MB vs ~1.6 GB with 4 workers) **Consequences:** - ✅ Telegram bot works perfectly - ✅ No SQLite cache conflicts - ✅ Cache stats endpoint works (no 502 errors) - ✅ Lower memory usage - ✅ Same performance (I/O bound, not CPU bound) - ⚠️ Cannot scale horizontally with multiple processes **Implementation:** - Configured in NSSM: `--workers 1` - NOT configurable in `.env` - Documented in `TELEGRAM-BOT-DEPLOYMENT.md` **Alternatives Considered:** - Separate bot service: More complexity, no significant benefit - Redis pub/sub for bot: Over-engineering for current scale **Performance Characteristics:** - Concurrent users: 100+ - Requests per minute: 1000+ - Response time: 50-200ms (DB query time) - Adequate for: 50-100 concurrent users --- ## ADR-003: IIS Sub-Application Deployment (2025-12-29) **Status:** Accepted **Context:** Application deployed at `/roa2web` path on IIS, not at root. **Decision:** Use Vite `base: '/roa2web/'` and axios `baseURL: import.meta.env.BASE_URL + 'api'` for all frontend API calls. **Consequences:** - ✅ Works correctly on production sub-path - ✅ Works correctly on development root path - ⚠️ All axios instances must use BASE_URL - ⚠️ Router must use base path **Implementation:** - `vite.config.js`: `base: process.env.NODE_ENV === 'production' ? '/roa2web/' : '/'` - All `axios.create()`: `baseURL: import.meta.env.BASE_URL + 'api/...'` - IIS `web.config`: Reverse proxy rules for `/api/*` → `localhost:8000` --- ## ADR-004: Pydantic v2 Migration (2025-12-29) **Status:** In Progress **Context:** Pydantic v2 changed `schema_extra` → `json_schema_extra`. **Decision:** Update all models incrementally to use `json_schema_extra`. **Implementation:** - ✅ `backend/modules/reports/models/trial_balance.py` updated - ✅ `backend/modules/data_entry/schemas/ocr.py` already correct - ⚠️ Warning eliminated in logs --- ## ADR-005: Web.config as Static File (2025-12-29) **Status:** Accepted **Context:** IIS requires `web.config` for URL rewrite rules, but it wasn't consistently deployed. **Decision:** 1. Store canonical `web.config` in `deployment/windows/config/` 2. Create `Create-WebConfig.ps1` script for manual creation 3. Include in deployment package **Consequences:** - ✅ Consistent configuration across deployments - ✅ Easy to recreate if deleted - ✅ Version controlled **Implementation:** - Canonical: `deployment/windows/config/web.config` - Deployed to: `C:\inetpub\wwwroot\roa2web\frontend\web.config` - Creation script: `Create-WebConfig.ps1` --- ## Future Decisions to Document ### Pending Considerations 1. **Horizontal Scaling:** - If needed: Load balancer + multiple app instances - Alternative: Separate bot service to allow multiple workers - Current decision: Single worker sufficient 2. **Cache Strategy:** - Current: SQLite L2 cache + memory L1 - Alternative: Redis for distributed cache - Decision: Keep SQLite for simplicity 3. **Database Connection Pool:** - Current: Shared pool across all modules - Sizing: 5-10 connections - Per-module pools: Not needed yet --- ## Decision Process When making architectural decisions: 1. **Document the context** - Why is this decision needed? 2. **List alternatives** - What other options exist? 3. **Explain rationale** - Why this option over others? 4. **Note consequences** - What are the trade-offs? 5. **Implementation details** - How is this implemented? 6. **Reference documentation** - Where to find more details? --- **Last Updated:** 2025-12-29