docs: cadrage initial Storytime (specs par jalon, roadmap, CLAUDE.md)
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>
This commit is contained in:
@@ -0,0 +1,150 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user