--- stepsCompleted: [1, 2, 3, 4, 5, 6, 7, 8] inputDocuments: - prd.md - prd-validation-report.md - product-brief-office_translator-2026-02-18.md workflowType: 'architecture' project_name: 'office_translator' user_name: 'Sepehr' date: '2026-02-18' lastStep: 8 status: 'complete' completedAt: '2026-02-19' --- # Architecture Decision Document _This document builds collaboratively through step-by-step discovery. Sections are appended as we work through each architectural decision together._ ## Project Context Analysis ### Requirements Overview **Functional Requirements:** | Catégorie | Count | Impact Architectural | |-----------|-------|---------------------| | Document Translation | FR1-FR8 | Cœur métier - module translation | | Format Preservation | FR9-FR16 | Critique - différenciateur produit | | User Management & Auth | FR17-FR22 | Module auth (JWT + API Keys) | | Subscription & Billing | FR23-FR28 | Module payment à refactorer | | API & Integration | FR29-FR35 | API REST versionnée /api/v1/ | | Webhooks | FR36-FR38, FR65-FR66 | Module webhooks (Fire & Forget) | | Admin Dashboard | FR39-FR45 | Module admin | | Web UI | FR46-FR49 | Frontend Next.js | | File Management | FR50-FR54 | Stockage éphémère /tmp | | Error Handling | FR55-FR57 | Cross-cutting concern | | Glossaries & Prompts | FR58-FR61 | **MVP Critique** - Justification abonnement Pro | | URL Ingestion | FR62-FR64 | Feature automation | **Non-Functional Requirements:** | Catégorie | NFRs | Décisions Architecturales Clés | |-----------|------|-------------------------------| | Performance | NFR1-NFR5 | Async processing, progress feedback < 500ms | | Security | NFR6-NFR11 | JWT + API Keys, HTTPS, secrets via env | | Reliability | NFR12-NFR14 | 0% HTTP 500, provider fallback, TTL cleanup | | Data Retention | NFR15-NFR17 | Zero retention, 60 min TTL, RGPD by design | | API Quality | NFR18-NFR21 | OpenAPI 3.0, JSON errors, rate limiting, versioning | **Scale & Complexity:** - Primary domain: Full-stack (Backend API + Frontend Web + Admin Dashboard) - Complexity level: Moyenne-Haute - Estimated architectural components: 5 modules ### Technical Constraints & Dependencies | Contrainte | Description | Impact | |------------|-------------|--------| | Brownfield | Refonte de l'existant | Architecture monolithique modulaire | | Solo Developer | Une seule personne | Pas de microservices, dette technique zéro | | Payment Module | Existant à refactorer | Clean Architecture pour module payment | | Stockage | Local éphémère /tmp | Pas de S3 pour MVP | | Hébergement | Préférence UE | RGPD compliance | | LLM Providers | Multi-provider (Ollama, OpenAI, compatibles) | Abstraction provider avec fallback | ### Cross-Cutting Concerns Identified | Concern | Modules Affectés | Stratégie | |---------|------------------|-----------| | Rate Limiting | API, Auth | Par utilisateur/tier, réponse 429 avec Retry-After | | Logging | Tous | Structuré, métadonnées uniquement (pas de contenu document) | | Error Handling | Tous | 0% HTTP 500, erreurs JSON structurées | | File Cleanup | Translation | TTL 60 min, hard delete après download | | Auth | API, Admin, Web | JWT (Web/Admin) + API Keys (Automation) | ## Starter Template Evaluation ### Primary Technology Domain **Full-stack Brownfield** — Refonte de l'existant avec stack technologique validée. ### Contexte Projet Ce n'est pas un projet greenfield. L'objectif est de **refactorer** une codebase existante vers une architecture propre (Clean Architecture) plutôt que de partir d'un starter template. ### Stack Technologique Confirmée | Couche | Technologie | Justification | |--------|-------------|---------------| | **Backend** | FastAPI (Python) | Écosystème Python, async natif, excellent pour API REST | | **Frontend** | Next.js 15 (App Router) | Déjà en place, Tailwind CSS inclus | | **Base de données** | PostgreSQL (prod) / SQLite (dev) | SaaS-ready, données persistantes (users, subscriptions, API keys) | | **ORM** | SQLAlchemy / SQLModel | Moderne, typé, compatible FastAPI | | **Rate Limiting** | Redis | Robuste, distribué, performant | | **Déploiement** | Docker + VPS | Reverse proxy (Traefik/Nginx), HTTPS/HSTS | ### Refactoring Backend - Clean Architecture **Structure cible par domaine:** ``` backend/ ├── modules/ │ ├── translation/ # Cœur métier │ │ ├── router.py │ │ ├── service.py │ │ └── schemas.py │ ├── auth/ # JWT + API Keys │ ├── payment/ # À refactorer │ ├── admin/ # Dashboard │ └── webhooks/ # Notifications ├── core/ │ ├── config.py # Pydantic BaseSettings │ ├── security.py │ └── database.py └── main.py # Point d'entrée épuré ``` ### Refactoring Frontend - Focus UX - Suppression code mort - Amélioration Core Web Vitals (LCP < 2.5s, INP < 200ms, CLS < 0.1) - UX cohérente (textes unifiés, parcours clairs) ### Décisions Architecturales Établies **Language & Runtime:** - Backend: Python 3.11+ avec FastAPI - Frontend: TypeScript avec Next.js 15 **Styling Solution:** - Tailwind CSS (déjà configuré) **Build Tooling:** - Backend: uv/pip avec pyproject.toml - Frontend: Next.js built-in (Turbopack) **Database & ORM:** - PostgreSQL (prod) / SQLite (dev) - SQLAlchemy 2.0 ou SQLModel **Infrastructure:** - Docker Compose pour orchestration locale - VPS avec reverse proxy pour production ## Core Architectural Decisions ### Decision Priority Analysis **Critical Decisions (Block Implementation):** - Auth library (PyJWT) - Database migrations (Alembic) - Error format (JSON structuré) **Important Decisions (Shape Architecture):** - State management frontend (TanStack Query) - Logging (structlog) - API documentation (Swagger + ReDoc) **Deferred Decisions (Post-MVP):** - Prometheus/Grafana monitoring - Zustand pour state local complexe ### Data Architecture | Décision | Choix | Version | Rationale | |----------|-------|---------|-----------| | Database | PostgreSQL (prod) / SQLite (dev) | 16+ / 3.x | SaaS-ready, abonnements, API keys | | ORM | SQLAlchemy 2.0 / SQLModel | 2.0+ | Typé, compatible FastAPI, mature | | Migrations | **Alembic** | Latest | Standard absolu SQLAlchemy, évolutivité schéma | | Validation | Pydantic v2 | 2.x | Natif FastAPI, validation automatique | ### Authentication & Security | Décision | Choix | Version | Rationale | |----------|-------|---------|-----------| | JWT Library | **PyJWT** | 2.x | Moderne, bien maintenu, plus standard que python-jose | | Password Hashing | **passlib[bcrypt]** | Latest | Éprouvé, standard industrie, parfait MVP | | API Key Generation | **secrets.token_urlsafe(32)** | Natif | 256-bit, URL-safe, simple, sécurisé | | Token Expiry | Access: 15min, Refresh: 7 days | — | PRD NFR6 | **Flux Auth:** ``` Web/Admin → JWT (Access + Refresh Tokens) API Automation → X-API-Key header (static key) ``` ### API & Communication Patterns | Décision | Choix | Rationale | |----------|-------|-----------| | REST Versioning | `/api/v1/` | PRD requirement, préserve intégrations | | Error Format | `{error, message, details?}` | Parsable par n8n, clair pour devs | | Documentation | **Swagger UI + ReDoc** | FastAPI génère gratuitement, Swagger=test, ReDoc=lecture | | Rate Limiting | Redis + sliding window | Par utilisateur/tier, réponse 429 + Retry-After | **Endpoints Principaux:** | Endpoint | Méthode | Description | |----------|---------|-------------| | `/api/v1/translate` | POST | Traduire un document | | `/api/v1/languages` | GET | Langues supportées | | `/api/v1/health` | GET | Santé du service | | `/api/v1/download/{id}` | GET | Télécharger fichier traduit | ### Frontend Architecture | Décision | Choix | Rationale | |----------|-------|-----------| | State Management (Server) | **TanStack Query** | Standard pour appels API Next.js, cache, revalidation | | State Management (Local) | React useState | Suffisant pour UI state, pas de Zustand nécessaire | | Component Model | Server Components default | Next.js 15 best practice, 'use client' si besoin | | Styling | Tailwind CSS | Déjà configuré | **Pattern Data Fetching:** ```tsx // Server Component (default) const data = await fetch('/api/v1/languages'); // Client Component avec TanStack Query const { data, isLoading } = useQuery({ queryKey: ['translations'], ... }); ``` ### Infrastructure & Deployment | Décision | Choix | Rationale | |----------|-------|-----------| | Logging | **structlog** | JSON structuré, lisible par Admin Dashboard | | Monitoring | **Simple health checks** | GET /health existant, pas d'over-engineering | | Containerization | Docker Compose | Backend + Frontend + Redis + DB | | Reverse Proxy | Traefik ou Nginx | HTTPS/HSTS, termination SSL | | Hosting | VPS unique | Solo dev, simple, coût maîtrisé | **Health Check Format:** ```json { "status": "healthy", "database": "connected", "redis": "connected", "providers": {"google": "ok", "deepl": "ok"} } ``` ### Decision Impact Analysis **Séquence d'Implémentation:** 1. Setup Alembic migrations 2. Core: Auth module (PyJWT + passlib) 3. Core: Translation module (providers abstraction) 4. API: REST endpoints + error format 5. Frontend: TanStack Query setup 6. Infra: structlog integration 7. Admin: Dashboard health metrics **Cross-Component Dependencies:** - Auth → DB (users, api_keys tables) - Translation → Redis (rate limiting, progress) - Admin → Logging (structlog consumption) - Webhooks → Translation (completion events) ## Implementation Patterns & Consistency Rules ### ⚠️ Règles Critiques (Non-Négociables) Ces règles DOIVENT être respectées par tous les agents AI pour éviter les conflits: ``` 🚨 NEXT.JS APP ROUTER - FICHIERS SPÉCIAUX page.tsx, layout.tsx, route.ts, loading.tsx, error.tsx → TOUJOURS en minuscules strictes → JAMAIS PascalCase (le framework plantera avec 404) ``` ### Naming Patterns #### Backend (Python/FastAPI) | Élément | Convention | Exemple | |---------|------------|---------| | Tables DB | snake_case, pluriel | `users`, `api_keys`, `translations` | | Colonnes DB | snake_case | `user_id`, `created_at`, `file_name` | | Foreign keys | `{table}_id` | `user_id`, `subscription_id` | | Index | `idx_{table}_{columns}` | `idx_users_email`, `idx_translations_user_id` | | Fichiers Python | snake_case | `translation_service.py`, `auth_router.py` | | Variables | snake_case | `user_id`, `file_path`, `translation_result` | | Fonctions | snake_case | `get_user_by_id()`, `translate_document()` | | Classes | PascalCase | `TranslationService`, `UserRepository` | | Constantes | UPPER_SNAKE | `MAX_FILE_SIZE`, `TTL_SECONDS`, `API_VERSION` | #### Frontend (TypeScript/Next.js) | Élément | Convention | Exemple | |---------|------------|---------| | **Fichiers spéciaux App Router** | **minuscules strictes** | `page.tsx`, `layout.tsx`, `route.ts`, `loading.tsx`, `error.tsx` | | Composants réutilisables | PascalCase | `UserCard.tsx`, `TranslationProgress.tsx` | | Utilitaires/lib | camelCase | `apiClient.ts`, `formatters.ts` | | Variables | camelCase | `userId`, `fileName`, `translationResult` | | Fonctions | camelCase | `getUserById()`, `handleFileUpload()` | | Composants (nom) | PascalCase | ``, `` | | Hooks | useCamelCase | `useTranslation()`, `useUser()` | | Types/Interfaces | PascalCase | `User`, `TranslationResponse`, `ApiError` | #### API (Communication Backend ↔ Frontend) | Élément | Convention | Exemple | |---------|------------|---------| | Endpoints REST | pluriel, kebab-case | `/api/v1/users`, `/api/v1/api-keys` | | Paramètres route | snake_case | `/api/v1/translations/{translation_id}` | | Query params | snake_case | `?user_id=123&file_type=xlsx` | | Headers | Pascal-Kebab | `X-API-Key`, `Content-Type`, `Authorization` | | **JSON fields** | **snake_case** | `user_id`, `created_at`, `file_name` | | Dates | ISO 8601 string | `"2024-01-15T10:30:00Z"` | ### API Response Formats #### Format Succès ```json { "data": { "id": "abc123", "file_name": "report.xlsx", "status": "completed" }, "meta": { "rate_limit_remaining": 45, "provider_used": "google", "processing_time_ms": 2340 } } ``` **Champ `meta` utilisé pour:** - Rate limiting info (`rate_limit_remaining`, `rate_limit_reset`) - Pagination (`page`, `per_page`, `total`) - Provider info (`provider_used`) - Performance (`processing_time_ms`) #### Format Erreur ```json { "error": "QUOTA_EXCEEDED", "message": "Limite quotidienne atteinte. Passez en Pro pour plus de traductions.", "details": { "current_usage": 5, "limit": 5, "reset_at": "2024-01-16T00:00:00Z" } } ``` **⚠️ Pas de champ `data` en cas d'erreur.** #### Codes Erreur Standards | Code | HTTP | Usage | |------|------|-------| | `INVALID_FORMAT` | 400 | Format fichier non supporté | | `QUOTA_EXCEEDED` | 429 | Limite tier atteinte | | `UNAUTHORIZED` | 401 | Token/API key invalide | | `FORBIDDEN` | 403 | Pas les droits | | `FILE_TOO_LARGE` | 413 | Fichier > max size | | `PROVIDER_ERROR` | 502 | Provider externe en erreur | ### Structure Patterns #### Organisation Tests | Couche | Emplacement | Exemple | |--------|-------------|---------| | Backend (Python) | `tests/` centralisé | `tests/test_translation_service.py` | | Frontend (TS/React) | Co-localisé | `components/UserCard.test.tsx` | #### Organisation Frontend (Colocation Pattern) ``` frontend/src/ ├── app/ # Next.js App Router │ ├── (auth)/ # Route group │ │ └── login/ │ │ ├── page.tsx # ⚠️ minuscule! │ │ ├── LoginForm.tsx # ⭐ Colocated component │ │ └── types.ts # ⭐ Colocated types │ ├── dashboard/ │ │ └── translate/ │ │ ├── page.tsx │ │ ├── FileUploader.tsx # ⭐ Colocated │ │ ├── useTranslation.ts # ⭐ Colocated hook │ │ └── types.ts │ ├── layout.tsx │ └── page.tsx ├── components/ │ └── ui/ # ⭐ Seulement composants GLOBAUX │ ├── Button.tsx │ └── Input.tsx ├── lib/ # ⭐ Seulement utilitaires GLOBAUX │ └── apiClient.ts └── providers/ └── QueryProvider.tsx ``` ### Process Patterns #### Error Handling **Backend:** ```python from fastapi import HTTPException raise HTTPException( status_code=400, detail={ "error": "INVALID_FORMAT", "message": "Format PDF non supporté", "details": {"accepted_formats": [".xlsx", ".docx", ".pptx"]} } ) ``` **Frontend:** ```typescript try { const result = await apiClient.translate(file); } catch (error) { // error.response.data = { error, message, details } toast.error(error.response.data.message); } ``` #### Loading States ```typescript // TanStack Query pattern const { data, isLoading, error } = useQuery({ queryKey: ['translation', id], queryFn: () => apiClient.getTranslation(id), }); if (isLoading) return ; if (error) return ; return ; ``` ### Enforcement Guidelines **Tous les agents AI DOIVENT:** 1. ✅ Respecter les conventions de nommage par langage 2. ✅ Utiliser les formats API définis (succès avec `data`+`meta`, erreur sans `data`) 3. ✅ Nommer les fichiers spéciaux Next.js en minuscules (`page.tsx`, pas `Page.tsx`) 4. ✅ Utiliser snake_case pour les champs JSON API 5. ✅ Retourner des erreurs structurées avec codes explicites 6. ✅ Placer les tests selon les conventions (centralisé Python, co-localisé TS) **Anti-Patterns à ÉVITER:** ```typescript // ❌ MAUVAIS - Fichier App Router en PascalCase app/Dashboard/Page.tsx // → 404 error! // ❌ MAUVAIS - camelCase dans JSON API { "fileName": "report.xlsx" } // ❌ MAUVAIS - data présent dans erreur { "data": null, "error": "..." } // ✅ BON app/dashboard/page.tsx { "file_name": "report.xlsx" } { "error": "...", "message": "..." } // pas de data ``` ### Pattern Examples **Exemple Requête API Complète:** ```http POST /api/v1/translate X-API-Key: sk_live_abc123... Content-Type: multipart/form-data file: report.xlsx source_lang: en target_lang: fr webhook_url: https://example.com/webhook ``` **Réponse Succès:** ```json { "data": { "id": "tr_abc123", "status": "processing", "file_name": "report.xlsx", "source_lang": "en", "target_lang": "fr" }, "meta": { "rate_limit_remaining": 49, "estimated_time_seconds": 12 } } ``` **Réponse Erreur:** ```json { "error": "QUOTA_EXCEEDED", "message": "Limite quotidienne atteinte (5/5 fichiers)", "details": { "current_usage": 5, "limit": 5, "tier": "free", "reset_at": "2024-01-16T00:00:00Z" } } ``` ## Project Structure & Boundaries ### ⚠️ Règle de Colocation (Frontend) ``` 🚨 NEXT.JS APP ROUTER - COLOCATION Les composants, hooks et types spécifiques à une page doivent vivre DANS le dossier de la route correspondante. → Un seul dossier par feature = tous les fichiers proches → Seuls les composants GLOBAUX vont dans src/components/ui/ ``` ### Complete Project Directory Structure ``` office_translator/ ├── docker-compose.yml ├── docker-compose.dev.yml ├── .env.example ├── .gitignore ├── README.md │ ├── backend/ │ ├── pyproject.toml │ ├── requirements.txt │ ├── alembic.ini │ ├── .env.example │ ├── Dockerfile │ │ │ ├── alembic/ │ │ ├── env.py │ │ ├── versions/ │ │ │ ├── 001_initial_users.py # User + tier field │ │ │ ├── 002_api_keys.py │ │ │ ├── 003_translations.py │ │ │ └── 004_glossaries.py │ │ └── script.py.mako │ │ │ ├── app/ │ │ ├── main.py │ │ ├── __init__.py │ │ │ │ │ ├── core/ │ │ │ ├── __init__.py │ │ │ ├── config.py # Pydantic BaseSettings │ │ │ ├── database.py # SQLAlchemy async engine │ │ │ ├── security.py # JWT, password hashing │ │ │ ├── logging.py # structlog setup │ │ │ └── exceptions.py # Custom exceptions │ │ │ │ │ ├── modules/ │ │ │ ├── __init__.py │ │ │ │ │ │ │ ├── auth/ │ │ │ │ ├── __init__.py │ │ │ │ ├── router.py # /api/v1/auth/* │ │ │ │ ├── service.py │ │ │ │ ├── schemas.py │ │ │ │ └── dependencies.py # get_current_user │ │ │ │ │ │ │ ├── translation/ │ │ │ │ ├── __init__.py │ │ │ │ ├── router.py # /api/v1/translate │ │ │ │ ├── service.py # Translation orchestration │ │ │ │ ├── ingest.py # ⭐ URL ingestion (FR62-FR64) │ │ │ │ ├── schemas.py │ │ │ │ ├── providers/ │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── base.py # Abstract provider │ │ │ │ │ ├── google.py │ │ │ │ │ ├── deepl.py │ │ │ │ │ ├── ollama.py │ │ │ │ │ └── openai.py │ │ │ │ └── processors/ │ │ │ │ ├── __init__.py │ │ │ │ ├── excel.py │ │ │ │ ├── word.py │ │ │ │ └── powerpoint.py │ │ │ │ │ │ │ ├── glossaries/ │ │ │ │ ├── __init__.py │ │ │ │ ├── router.py # /api/v1/glossaries │ │ │ │ ├── service.py │ │ │ │ └── schemas.py │ │ │ │ │ │ │ ├── webhooks/ │ │ │ │ ├── __init__.py │ │ │ │ ├── router.py │ │ │ │ ├── service.py # ⭐ Fire & Forget (FR36-FR38) │ │ │ │ └── schemas.py │ │ │ │ │ │ │ ├── admin/ │ │ │ │ ├── __init__.py │ │ │ │ ├── router.py # /api/v1/admin/* │ │ │ │ ├── service.py │ │ │ │ └── schemas.py │ │ │ │ │ │ │ └── payment/ │ │ │ ├── __init__.py │ │ │ ├── router.py # /api/v1/payments │ │ │ ├── service.py │ │ │ └── schemas.py │ │ │ │ │ ├── models/ │ │ │ ├── __init__.py │ │ │ ├── user.py # ⭐ user.tier (free/pro) direct │ │ │ ├── api_key.py │ │ │ ├── translation.py │ │ │ └── glossary.py │ │ │ │ │ └── middleware/ │ │ ├── __init__.py │ │ ├── rate_limit.py # Redis + user.tier check │ │ └── error_handler.py │ │ │ └── tests/ │ ├── __init__.py │ ├── conftest.py │ ├── test_translation_service.py │ ├── test_ingest.py # ⭐ URL ingestion tests │ ├── test_auth_router.py │ ├── test_rate_limit.py │ └── test_providers/ │ ├── test_google_provider.py │ └── test_deepl_provider.py │ ├── frontend/ │ ├── package.json │ ├── next.config.js │ ├── tailwind.config.ts │ ├── tsconfig.json │ ├── .env.local.example │ ├── Dockerfile │ │ │ ├── src/ │ │ ├── app/ │ │ │ ├── layout.tsx # Root layout │ │ │ ├── page.tsx # Landing page │ │ │ ├── globals.css │ │ │ │ │ │ │ ├── (auth)/ │ │ │ │ ├── layout.tsx │ │ │ │ ├── login/ │ │ │ │ │ ├── page.tsx │ │ │ │ │ ├── LoginForm.tsx # ⭐ Colocated │ │ │ │ │ └── types.ts # ⭐ Colocated │ │ │ │ └── register/ │ │ │ │ ├── page.tsx │ │ │ │ ├── RegisterForm.tsx # ⭐ Colocated │ │ │ │ └── types.ts │ │ │ │ │ │ │ ├── dashboard/ │ │ │ │ ├── layout.tsx │ │ │ │ ├── page.tsx │ │ │ │ ├── translate/ │ │ │ │ │ ├── page.tsx │ │ │ │ │ ├── FileUploader.tsx # ⭐ Colocated │ │ │ │ │ ├── TranslationProgress.tsx │ │ │ │ │ ├── LanguageSelector.tsx │ │ │ │ │ ├── useTranslation.ts # ⭐ Colocated hook │ │ │ │ │ └── types.ts # ⭐ Colocated types │ │ │ │ ├── api-keys/ │ │ │ │ │ ├── page.tsx │ │ │ │ │ ├── ApiKeyManager.tsx # ⭐ Colocated │ │ │ │ │ ├── useApiKeys.ts │ │ │ │ │ └── types.ts │ │ │ │ └── glossaries/ │ │ │ │ ├── page.tsx │ │ │ │ ├── GlossaryEditor.tsx # ⭐ Colocated │ │ │ │ ├── useGlossaries.ts │ │ │ │ └── types.ts │ │ │ │ │ │ │ └── admin/ │ │ │ ├── layout.tsx │ │ │ ├── page.tsx │ │ │ ├── users/ │ │ │ │ ├── page.tsx │ │ │ │ ├── UserTable.tsx # ⭐ Colocated │ │ │ │ ├── TierBadge.tsx │ │ │ │ └── types.ts │ │ │ └── system/ │ │ │ ├── page.tsx │ │ │ ├── HealthStatus.tsx # ⭐ Colocated │ │ │ └── types.ts │ │ │ │ │ ├── components/ │ │ │ └── ui/ # ⭐ Seulement composants GLOBAUX │ │ │ ├── Button.tsx │ │ │ ├── Input.tsx │ │ │ ├── Card.tsx │ │ │ ├── Modal.tsx │ │ │ ├── LoadingSpinner.tsx │ │ │ └── ErrorMessage.tsx │ │ │ │ │ ├── lib/ # ⭐ Seulement utilitaires GLOBAUX │ │ │ ├── apiClient.ts # Fetch wrapper, error handling │ │ │ ├── auth.ts # Token management │ │ │ └── constants.ts # App-wide constants │ │ │ │ │ └── providers/ │ │ └── QueryProvider.tsx # TanStack Query setup │ │ │ ├── __tests__/ │ │ └── lib/ │ │ └── apiClient.test.ts │ │ │ └── public/ │ ├── favicon.ico │ └── logo.svg │ └── scripts/ ├── deploy.sh └── backup-db.sh ``` ### Architectural Boundaries #### API Boundaries | Frontière | Description | |-----------|-------------| | `/api/v1/translate` | Public endpoint (auth required) | | `/api/v1/admin/*` | Admin-only (role check) | | `/api/v1/glossaries` | Pro-only (tier check) | | Rate Limiting | Middleware → Redis → user.tier | #### Data Boundaries **Modèle User (simplifié pour Rate Limiting):** ```python # models/user.py class User(Base): __tablename__ = "users" id: Mapped[UUID] email: Mapped[str] hashed_password: Mapped[str] tier: Mapped[str] = mapped_column(default="free") # ⭐ "free" | "pro" daily_translation_count: Mapped[int] = mapped_column(default=0) # ... timestamps ``` **Rate Limiting Logic:** ```python # middleware/rate_limit.py FREE_TIER_LIMIT = 5 # files per day PRO_TIER_LIMIT = None # unlimited if user.tier == "free" and user.daily_translation_count >= FREE_TIER_LIMIT: raise QuotaExceededError() ``` ### Requirements to Structure Mapping | Module PRD | Backend | Frontend | |------------|---------|----------| | Translation (FR1-FR8) | `modules/translation/` | `app/dashboard/translate/` | | Format Preservation (FR9-FR16) | `modules/translation/processors/` | — | | URL Ingestion (FR62-FR64) | `modules/translation/ingest.py` | — | | Auth (FR17-FR22) | `modules/auth/` | `app/(auth)/` | | Payment (FR23-FR28) | `modules/payment/` | `app/dashboard/` | | API Keys (FR29-FR31) | `models/api_key.py` | `app/dashboard/api-keys/` | | Webhooks (FR36-FR38) | `modules/webhooks/` | — | | Admin (FR39-FR45) | `modules/admin/` | `app/admin/` | | Glossaries (FR58-FR61) | `modules/glossaries/` | `app/dashboard/glossaries/` | ### Integration Points #### Internal Communication ``` Frontend (Next.js) ↓ fetch /api/v1/* Backend (FastAPI) ↓ SQLAlchemy Database (PostgreSQL) ↓ Redis (Rate Limiting + Cache) ``` #### External Integrations | Service | Module | Usage | |---------|--------|-------| | Google Translate | `providers/google.py` | Classic translation | | DeepL | `providers/deepl.py` | Classic translation | | Ollama | `providers/ollama.py` | LLM translation | | OpenAI | `providers/openai.py` | LLM translation | | Webhooks (User) | `webhooks/service.py` | POST to user URL | ### Development Workflow **Démarrage Local:** ```bash docker-compose -f docker-compose.dev.yml up -d # Backend: http://localhost:8000 # Frontend: http://localhost:3000 # API Docs: http://localhost:8000/docs ``` **Migrations:** ```bash cd backend alembic revision --autogenerate -m "description" alembic upgrade head ``` ## Architecture Validation Results ### Coherence Validation ✅ **Decision Compatibility:** | Stack | Compatibilité | |-------|---------------| | FastAPI + Pydantic v2 | ✅ Natif, parfait | | SQLAlchemy 2.0 + Alembic | ✅ Standard, éprouvé | | PyJWT + passlib[bcrypt] | ✅ Stack auth standard | | Next.js 15 + TanStack Query | ✅ Moderne, best practice | | PostgreSQL + Redis | ✅ Combo classique SaaS | | structlog + JSON logs | ✅ Cohérent avec Admin Dashboard | **Pattern Consistency:** - snake_case DB/API ↔ camelCase Frontend (convention par langage) ✅ - App Router fichiers minuscules ↔ Colocation components/hooks ✅ - Response wrapper {data, meta} ↔ Error format {error, message} ✅ **Structure Alignment:** - Backend: Clean Architecture modulaire → 5 modules métier ✅ - Frontend: Colocation par feature → fichiers proches de leur usage ✅ - Mapping FRs → Emplacements documenté ✅ ### Requirements Coverage Validation ✅ **Functional Requirements Coverage (66 FRs):** | Catégorie | FRs | Status | |-----------|-----|--------| | Document Translation | FR1-FR8 | ✅ Couvert | | Format Preservation | FR9-FR16 | ✅ Couvert | | User Management & Auth | FR17-FR22 | ✅ Couvert | | Subscription & Billing | FR23-FR28 | ✅ Couvert | | API & Integration | FR29-FR35 | ✅ Couvert | | Webhooks | FR36-FR38, FR65-FR66 | ✅ Couvert | | Admin Dashboard | FR39-FR45 | ✅ Couvert | | Web UI | FR46-FR49 | ✅ Couvert | | File Management | FR50-FR54 | ✅ Couvert | | Error Handling | FR55-FR57 | ✅ Couvert | | Glossaries & Prompts | FR58-FR61 | ✅ Couvert (MVP) | | URL Ingestion | FR62-FR64 | ✅ Couvert | **Non-Functional Requirements Coverage (21 NFRs):** | Catégorie | NFRs | Status | |-----------|------|--------| | Performance | NFR1-NFR5 | ✅ Async + TanStack Query | | Security | NFR6-NFR11 | ✅ JWT + API Keys + HTTPS | | Reliability | NFR12-NFR14 | ✅ Error handler + fallback | | Data Retention | NFR15-NFR17 | ✅ TTL 60min + zero retention | | API Quality | NFR18-NFR21 | ✅ OpenAPI + JSON errors + versioning | ### Implementation Readiness Validation ✅ **Decision Completeness:** - [x] Technologies avec versions documentées - [x] Patterns de nommage complets - [x] Formats API (succès + erreur) - [x] Règles critiques (App Router, colocation) **Structure Completeness:** - [x] Arbre complet backend + frontend - [x] Fichiers de configuration définis - [x] Migrations Alembic nommées - [x] Tests organisés **Pattern Completeness:** - [x] Naming conventions - [x] API response formats - [x] Error codes - [x] Rate limiting logic ### Gap Analysis **Critical Gaps:** Aucun ✅ **Important Gaps:** Aucun ✅ **Nice-to-Have (Post-MVP):** - CI/CD pipeline (peut être défini lors implémentation) - PDF support (PRD: future feature) - Prometheus/Grafana (health checks suffisent) ### Architecture Completeness Checklist **✅ Requirements Analysis** - [x] Project context analysé - [x] Scale et complexité évalués - [x] Contraintes techniques identifiées - [x] Cross-cutting concerns mappés **✅ Architectural Decisions** - [x] Décisions critiques documentées avec versions - [x] Stack technologique complète - [x] Patterns d'intégration définis - [x] Considérations performance adressées **✅ Implementation Patterns** - [x] Naming conventions établies - [x] Structure patterns définis - [x] Communication patterns spécifiés - [x] Process patterns documentés **✅ Project Structure** - [x] Directory structure complète - [x] Component boundaries établies - [x] Integration points mappés - [x] Requirements → Structure mapping ### Architecture Readiness Assessment **Overall Status:** 🟢 **READY FOR IMPLEMENTATION** **Confidence Level:** HIGH **Key Strengths:** - Stack technologique cohérente et moderne - Clean Architecture modulaire (maintenabilité) - Colocation frontend (DX excellent pour solo dev) - 100% FRs/NFRs couverts - Patterns anti-conflit documentés **Areas for Future Enhancement:** - CI/CD pipeline automation - Advanced monitoring (Prometheus/Grafana) - PDF format support - Team collaboration features ### Implementation Handoff **AI Agent Guidelines:** 1. ✅ Suivre toutes les décisions architecturales exactement 2. ✅ Utiliser les patterns d'implémentation de manière cohérente 3. ✅ Respecter la structure projet et les boundaries 4. ✅ Se référer à ce document pour toute question architecturale **⚠️ Règles Critiques à Respecter:** ``` 🚨 NEXT.JS: page.tsx, layout.tsx TOUJOURS minuscules 🚨 COLOCATION: Components/hooks/types dans le dossier de leur page 🚨 API JSON: snake_case (pas camelCase) 🚨 RESPONSE: {data, meta} succès / {error, message} erreur ``` **First Implementation Priority:** 1. Setup Alembic migrations (001_initial_users.py) 2. Core: Auth module (PyJWT + passlib) 3. Core: Translation module (providers abstraction) 4. API: REST endpoints /api/v1/* 5. Frontend: TanStack Query + colocation structure --- *Document généré via workflow BMAD create-architecture — 2026-02-18*