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>
33 KiB
stepsCompleted, inputDocuments, workflowType, project_name, user_name, date, lastStep, status, completedAt
| stepsCompleted | inputDocuments | workflowType | project_name | user_name | date | lastStep | status | completedAt | |||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
architecture | office_translator | Sepehr | 2026-02-18 | 8 | complete | 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:
// 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:
{
"status": "healthy",
"database": "connected",
"redis": "connected",
"providers": {"google": "ok", "deepl": "ok"}
}
Decision Impact Analysis
Séquence d'Implémentation:
- Setup Alembic migrations
- Core: Auth module (PyJWT + passlib)
- Core: Translation module (providers abstraction)
- API: REST endpoints + error format
- Frontend: TanStack Query setup
- Infra: structlog integration
- 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
{
"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
{
"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:
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:
try {
const result = await apiClient.translate(file);
} catch (error) {
// error.response.data = { error, message, details }
toast.error(error.response.data.message);
}
Loading States
// 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:
- ✅ Respecter les conventions de nommage par langage
- ✅ Utiliser les formats API définis (succès avec
data+meta, erreur sansdata) - ✅ Nommer les fichiers spéciaux Next.js en minuscules (
page.tsx, pasPage.tsx) - ✅ Utiliser snake_case pour les champs JSON API
- ✅ Retourner des erreurs structurées avec codes explicites
- ✅ Placer les tests selon les conventions (centralisé Python, co-localisé TS)
Anti-Patterns à ÉVITER:
// ❌ 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:
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:
{
"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:
{
"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):
# 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:
# 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:
docker-compose -f docker-compose.dev.yml up -d
# Backend: http://localhost:8000
# Frontend: http://localhost:3000
# API Docs: http://localhost:8000/docs
Migrations:
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:
- Technologies avec versions documentées
- Patterns de nommage complets
- Formats API (succès + erreur)
- Règles critiques (App Router, colocation)
Structure Completeness:
- Arbre complet backend + frontend
- Fichiers de configuration définis
- Migrations Alembic nommées
- Tests organisés
Pattern Completeness:
- Naming conventions
- API response formats
- Error codes
- 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
- Project context analysé
- Scale et complexité évalués
- Contraintes techniques identifiées
- Cross-cutting concerns mappés
✅ Architectural Decisions
- Décisions critiques documentées avec versions
- Stack technologique complète
- Patterns d'intégration définis
- Considérations performance adressées
✅ Implementation Patterns
- Naming conventions établies
- Structure patterns définis
- Communication patterns spécifiés
- Process patterns documentés
✅ Project Structure
- Directory structure complète
- Component boundaries établies
- Integration points mappés
- 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:
- ✅ Suivre toutes les décisions architecturales exactement
- ✅ Utiliser les patterns d'implémentation de manière cohérente
- ✅ Respecter la structure projet et les boundaries
- ✅ 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:
- Setup Alembic migrations (001_initial_users.py)
- Core: Auth module (PyJWT + passlib)
- Core: Translation module (providers abstraction)
- API: REST endpoints /api/v1/*
- Frontend: TanStack Query + colocation structure
Document généré via workflow BMAD create-architecture — 2026-02-18