16fd4c8c36
Lecteur d'histoires cadenassé pour le coucher (Android/Flutter). - CLAUDE.md : principes craftsmanship/TDD/clean code/clean archi + decisions techniques - ROADMAP.md : suivi haut niveau des 7 jalons, a tenir a jour par etape - docs/specs/ : specs completes decoupees par jalon, etapes en sous-fichiers - .gitignore Flutter (pubspec.lock versionne, projet applicatif) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
151 lines
7.9 KiB
Markdown
151 lines
7.9 KiB
Markdown
# Storytime — Guide projet & principes d'ingénierie
|
|
|
|
> Lecteur d'histoires audio cadenassé pour le coucher, sur tablette Android.
|
|
> L'enfant écoute des histoires (podcasts) sans pouvoir sortir de l'application ;
|
|
> le parent gère les sources, le code parental et les limites.
|
|
|
|
Ce fichier est le contrat d'ingénierie du projet. **Toute contribution doit le respecter.**
|
|
|
|
---
|
|
|
|
## 1. Le produit en une page
|
|
|
|
- **Plateforme** : Android uniquement. **Flutter / Dart**.
|
|
- **Lecture** : streaming (pas de hors-ligne en v1).
|
|
- **Deux mondes** :
|
|
- **Espace enfant** (par défaut, verrouillé) : liste de titres d'histoires, gros boutons lecture/pause/suivant.
|
|
- **Espace parent** (protégé par code 4 chiffres) : gestion des podcasts, limites, réglages.
|
|
- **Verrouillage** : Screen Pinning (épinglage natif Android).
|
|
|
|
### Contraintes connues (à ne jamais oublier)
|
|
- L'épinglage empêche la **sortie facile**, mais la sortie réelle repose sur le **mécanisme natif Android + le PIN de la tablette**, PAS sur notre code parental. C'est un garde-fou enfant, pas un coffre-fort. Ne jamais promettre plus dans l'UI ou les messages.
|
|
- L'épinglage **ne survit pas à un redémarrage** de la tablette.
|
|
- Le code parental protège **l'accès aux écrans parent** et la **reprise après limite**, pas le désépinglage système.
|
|
|
|
---
|
|
|
|
## 2. Philosophie de développement
|
|
|
|
Le projet suit le **Software Craftsmanship** : code soigné, testé, simple, qui se lit comme une explication du domaine.
|
|
|
|
### TDD — non négociable
|
|
Cycle **Red → Green → Refactor** pour toute fonctionnalité ou correction :
|
|
1. **Red** : écrire un test qui décrit le comportement attendu. Le lancer, vérifier qu'il échoue **pour la bonne raison**.
|
|
2. **Green** : écrire le minimum de code pour le faire passer.
|
|
3. **Refactor** : nettoyer sans changer le comportement, tests toujours verts.
|
|
|
|
Ne jamais écrire de code de production sans test qui le justifie. Les seules exceptions : code purement déclaratif (constantes, thèmes) et glue framework triviale — et même là, le comportement observable se teste.
|
|
|
|
### Clean Code
|
|
- Noms qui révèlent l'intention ; pas d'abréviations obscures.
|
|
- Fonctions courtes, une seule raison d'exister.
|
|
- Pas de commentaire qui paraphrase le code ; un commentaire explique le **pourquoi**, jamais le **quoi**.
|
|
- Pas de nombre/chaîne magique : constantes nommées.
|
|
- Gestion d'erreur explicite (voir `Result`/`Either`, §4).
|
|
- **Boy Scout Rule** : laisser le code un peu plus propre qu'on ne l'a trouvé, sans dérive hors-sujet.
|
|
|
|
### YAGNI
|
|
On n'implémente que ce que le jalon courant exige. Pas d'abstraction « au cas où ».
|
|
|
|
---
|
|
|
|
## 3. Clean Architecture
|
|
|
|
Dépendances dirigées **vers le domaine**. Le domaine ne connaît ni Flutter, ni HTTP, ni la base de données.
|
|
|
|
```
|
|
Presentation ─────► Application (use cases) ─────► Domain ◄───── Data (infra)
|
|
(UI/Riverpod) (orchestration) (cœur métier) (impl. dépôts)
|
|
```
|
|
|
|
### Couches
|
|
- **Domain** (`domain/`) — entités, value objects, interfaces de dépôts (abstraites), erreurs métier. **Zéro dépendance externe.** Dart pur.
|
|
- **Application** (`application/`) — *use cases* : une classe = une intention métier (`PlayStoryUseCase`, `AddPodcastUseCase`). Orchestrent le domaine via les interfaces de dépôts. Dart pur.
|
|
- **Data** (`data/`) — implémentations concrètes des dépôts, *data sources* (HTTP iTunes, parsing RSS, SQLite, secure storage), DTO/mappers. Dépend du domaine, jamais l'inverse.
|
|
- **Presentation** (`presentation/`) — écrans, widgets, contrôleurs d'état (Riverpod Notifiers). Appelle les use cases. Ne contient pas de logique métier.
|
|
|
|
### Règle de dépendance
|
|
Une couche ne dépend que de couches plus internes. **Le domaine n'importe jamais `package:flutter`, `package:http`, `sqflite`, etc.** Toute violation est un bug d'architecture.
|
|
|
|
### Injection de dépendances
|
|
**Riverpod** comme conteneur DI + gestion d'état. Les use cases et dépôts sont fournis via des `Provider`. En test, on *override* les providers par des doubles.
|
|
|
|
---
|
|
|
|
## 4. Conventions de code
|
|
|
|
- **État/DI** : Riverpod (`Notifier`/`AsyncNotifier`).
|
|
- **Immutabilité** : modèles immuables (`freezed` ou `copyWith` manuel pour les entités simples).
|
|
- **Erreurs** : pas d'exception qui traverse les couches en silence. Le domaine renvoie un type `Result<Success, Failure>` (ou `Either`). Les `Failure` sont des types métier (`NetworkFailure`, `InvalidFeedFailure`, …). Les exceptions techniques sont capturées dans la couche `data` et converties en `Failure`.
|
|
- **Asynchrone** : `Future`/`Stream` typés ; pas de `dynamic`.
|
|
- **Lint** : `flutter_lints` + règles strictes activées (`analysis_options.yaml`). Le projet doit rester **0 warning**.
|
|
- **Format** : `dart format` ; pas de débat de style.
|
|
|
|
### Arborescence cible
|
|
```
|
|
lib/
|
|
core/ # transverse : Result, erreurs de base, constantes, thème, router
|
|
features/
|
|
locking/ # verrouillage / épinglage
|
|
domain/
|
|
application/
|
|
data/
|
|
presentation/
|
|
playback/ # lecture audio
|
|
podcasts/ # recherche, RSS, abonnements
|
|
parental/ # code parental, accès espace parent
|
|
limits/ # minuterie, compteur d'histoires, avertissements
|
|
main.dart
|
|
test/ # miroir de lib/ ; tests unitaires & use cases
|
|
test/widget/ # tests de widgets
|
|
integration_test/ # tests d'intégration (épinglage, parcours bout-en-bout)
|
|
```
|
|
|
|
Feature-first, puis découpage en couches dans chaque feature. **Un fichier = une responsabilité.** Quand un fichier grossit, c'est le signal qu'il fait trop de choses : on le scinde.
|
|
|
|
---
|
|
|
|
## 5. Stratégie de test
|
|
|
|
| Niveau | Cible | Outils |
|
|
|--------|-------|--------|
|
|
| Unitaire | Domain + use cases (la majorité des tests) | `flutter_test`, `mocktail` |
|
|
| Widget | Écrans/widgets isolés | `flutter_test` |
|
|
| Intégration | Épinglage réel, parcours enfant/parent | `integration_test` sur appareil/émulateur |
|
|
|
|
- Les tests de domaine/use cases ne touchent **jamais** au réseau, au disque ni à Flutter.
|
|
- Les dépendances externes (HTTP, RSS, stockage, plugin d'épinglage) sont derrière des interfaces, *mockées* en test.
|
|
- **Le jalon 1 (épinglage) exige une validation sur appareil réel** : un test/POC manuel documenté en plus des tests automatisés.
|
|
|
|
---
|
|
|
|
## 6. Processus & suivi
|
|
|
|
- **`docs/specs/`** : la spec, découpée par **jalon** (un dossier par jalon). Chaque jalon a un `README.md` (objectif + DoD) et ses **étapes en sous-fichiers numérotés**.
|
|
- **`ROADMAP.md`** : vue d'ensemble de l'avancement. **À mettre à jour à la fin de chaque étape** (cocher la case, dater, noter les écarts). C'est la source de vérité du « où on en est ».
|
|
- On traite **un jalon à la fois**, **une étape à la fois**, en TDD.
|
|
- **Definition of Done** d'une étape : code + tests verts + 0 warning lint + `ROADMAP.md` à jour.
|
|
|
|
### Rituel à chaque étape terminée
|
|
1. Tous les tests de l'étape passent.
|
|
2. `flutter analyze` ne renvoie aucun warning.
|
|
3. Cocher l'étape dans `ROADMAP.md` (avec la date du jour).
|
|
4. Si un écart avec la spec est apparu, le noter dans le fichier d'étape concerné.
|
|
|
|
---
|
|
|
|
## 7. Décisions techniques figées
|
|
|
|
| Sujet | Choix | Raison |
|
|
|-------|-------|--------|
|
|
| Langage/UI | Flutter / Material 3 | Mono-plateforme Android, connu de l'auteur |
|
|
| État + DI | Riverpod | Testable, override facile en test |
|
|
| Audio | `just_audio` + `audio_service` | Lecture + contrôle arrière-plan |
|
|
| Épinglage | `kiosk_mode` (plugin), fallback platform channel Kotlin | API native Screen Pinning |
|
|
| Recherche podcasts | API iTunes Search (gratuite, sans clé) | Annuaire public |
|
|
| RSS | `dart_rss` (ou `webfeed`) | Parsing de flux |
|
|
| Persistance | `sqflite`/`drift` (abonnements) + `flutter_secure_storage` (code haché) | Local, sûr |
|
|
| Lecture seule réglages | `shared_preferences` | Compteurs/limites |
|
|
|
|
Tout changement de cette table doit être justifié dans le commit et reflété ici.
|