Files
office_translator/_bmad-output/planning-artifacts/architecture.md
Sepehr Ramezani 26bd096a06 feat: production deployment - full update with providers, admin, glossaries, pricing, tests
Major changes across backend, frontend, infrastructure:
- Provider system with model selection (Google, DeepL, OpenAI, Ollama, Google Cloud)
- Admin panel: user management, pricing, settings
- Glossary system with CSV import/export
- Subscription and tier quota management
- Security hardening (rate limiting, API key auth, path traversal fixes)
- Docker compose for dev, prod, and IONOS deployment
- Alembic migrations for new tables
- Frontend: dashboard, pricing page, landing page, i18n (en/fr)
- Test suite and verification scripts

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-04-25 15:01:47 +02:00

986 lines
33 KiB
Markdown

---
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 | `<UserCard />`, `<TranslationProgress />` |
| 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 <LoadingSpinner />;
if (error) return <ErrorMessage error={error} />;
return <TranslationResult data={data} />;
```
### 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*