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>
986 lines
33 KiB
Markdown
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*
|