refactor(adr): remplacer Firebase par implémentation directe APNS/FCM

Remplace toutes les références au SDK Firebase par une implémentation
directe des APIs APNS (iOS) et FCM (Android) pour éliminer le vendor
lock-in et assurer la cohérence avec la stratégie self-hosted.

Modifications :
- ADR-017 : Architecture notifications avec APNS/FCM direct
- ADR-018 : Remplacement firebase.google.com/go par sideshow/apns2 + oauth2
- ADR-020 : Remplacement firebase_messaging par flutter_apns + flutter_fcm
- Règles métier 09 & 14 : Mise à jour références coûts notifications

Avantages :
- Aucun vendor lock-in (code 100% maîtrisé)
- Cohérence avec ADR-008 (self-hosted) et ADR-015 (souveraineté)
- Gratuit sans limite (APNS/FCM natifs)
- APIs standard HTTP/2 et OAuth2
This commit is contained in:
jpgiannetti
2026-02-02 21:36:59 +01:00
parent b132fb957d
commit 6ba0688f87
5 changed files with 142 additions and 96 deletions

View File

@@ -14,14 +14,14 @@ ADR-002 spécifie HLS pour tout le streaming audio, mais HLS est un protocole un
Architecture hybride en **2 phases** : Architecture hybride en **2 phases** :
### Phase 1 (MVP) : WebSocket + Firebase Cloud Messaging ### Phase 1 (MVP) : WebSocket + APNS/FCM Direct
``` ```
[App Mobile] → [WebSocket] → [Backend Go] [App Mobile] → [WebSocket] → [Backend Go]
[PostGIS Worker] [PostGIS Worker]
[Firebase FCM / APNS] [APNS / FCM Direct API]
[Push Notification] [Push Notification]
``` ```
@@ -31,7 +31,7 @@ Architecture hybride en **2 phases** :
2. L'app envoie sa position GPS toutes les 30s via WebSocket 2. L'app envoie sa position GPS toutes les 30s via WebSocket
3. Un worker backend (goroutine) interroge PostGIS toutes les 30s : 3. Un worker backend (goroutine) interroge PostGIS toutes les 30s :
```sql ```sql
SELECT poi.*, users.fcm_token SELECT poi.*, users.push_token, users.platform
FROM points_of_interest poi FROM points_of_interest poi
JOIN user_locations users ON ST_DWithin( JOIN user_locations users ON ST_DWithin(
poi.geom, poi.geom,
@@ -41,7 +41,7 @@ Architecture hybride en **2 phases** :
WHERE users.notifications_enabled = true WHERE users.notifications_enabled = true
AND users.last_update > NOW() - INTERVAL '5 minutes' AND users.last_update > NOW() - INTERVAL '5 minutes'
``` ```
4. Si proximité détectée → envoi de push notification via Firebase (Android) ou APNS (iOS) 4. Si proximité détectée → envoi de push notification via FCM (Android) ou APNS (iOS)
5. Utilisateur clique → app s'ouvre → HLS démarre l'audio (ADR-002) 5. Utilisateur clique → app s'ouvre → HLS démarre l'audio (ADR-002)
**Limitations MVP** : **Limitations MVP** :
@@ -78,11 +78,11 @@ Architecture hybride en **2 phases** :
| Provider | Fiabilité | Coût MVP | Coût 100K users | Self-hosted | Vendor lock-in | Verdict | | Provider | Fiabilité | Coût MVP | Coût 100K users | Self-hosted | Vendor lock-in | Verdict |
|----------|-----------|----------|-----------------|-------------|----------------|---------| |----------|-----------|----------|-----------------|-------------|----------------|---------|
| **Firebase (choix)** | 99.95% | **0€** | **0€** | ❌ Non | 🔴 Fort (Google) | ✅ Optimal MVP | | **APNS/FCM Direct (choix)** | 99.95% | **0€** | **0€** | ✅ Oui | 🟢 Aucun | ✅ Optimal |
| OneSignal | 99.95% | 0€ | 500€/mois | ❌ Non | 🔴 Fort | ❌ Plus cher | | OneSignal | 99.95% | 0€ | 500€/mois | ❌ Non | 🔴 Fort | ❌ Plus cher |
| Pusher Beams | 99.9% | 0€ | 300€/mois | ❌ Non | 🔴 Fort | ❌ Niche | | Pusher Beams | 99.9% | 0€ | 300€/mois | ❌ Non | 🔴 Fort | ❌ Niche |
| Custom WS + APNS/FCM | Votre charge | 5€ | 100€+ | ✅ Oui | 🟢 Aucun | ⚠️ Complexe | | Firebase SDK | 99.95% | 0€ | 0€ | ❌ Non | 🔴 Fort (Google) | ❌ Vendor lock-in |
| Novu (open source) | 99.9% | 15€ | 50€ | ✅ Oui | 🟢 Aucun | 🟡 Phase 2 | | Novu (open source) | 99.9% | 15€ | 50€ | ✅ Oui | 🟢 Aucun | ❌ Overhead inutile |
| Brevo API | 99.9% | 0€ | 49€ | ✅ Oui | 🟢 Aucun | ❌ Email seulement | | Brevo API | 99.9% | 0€ | 49€ | ✅ Oui | 🟢 Aucun | ❌ Email seulement |
## Justification ## Justification
@@ -93,43 +93,40 @@ Architecture hybride en **2 phases** :
- **Batterie** : Connexion persistante optimisée par l'OS mobile - **Batterie** : Connexion persistante optimisée par l'OS mobile
- **Bi-directionnel** : Backend peut envoyer des mises à jour instantanées (ex: "nouveau POI créé par un créateur que tu suis") - **Bi-directionnel** : Backend peut envoyer des mises à jour instantanées (ex: "nouveau POI créé par un créateur que tu suis")
### Pourquoi Firebase FCM et pas implémentation custom ? ### Pourquoi implémentation directe APNS/FCM et pas SDK Firebase ?
- **Gratuit** : 10M notifications/mois (largement suffisant jusqu'à 100K utilisateurs)
- **Fiabilité** : Infrastructure Google avec 99.95% uptime
- **Batterie** : Utilise les mécanismes système (Google Play Services)
- **Cross-platform** : API unifiée iOS/Android
### Incohérence acceptée : Firebase vs self-hosted (ADR-008, ADR-015)
**Problème** : RoadWave promeut 100% self-hosted + souveraineté française, mais Firebase = dépendance Google Cloud.
**Réalité technique** : Notifications natives requièrent obligatoirement Google/Apple **Réalité technique** : Notifications natives requièrent obligatoirement Google/Apple
- **APNS (Apple)** : Seul protocole pour notifications iOS → dépendance Apple inévitable - **APNS (Apple)** : Seul protocole pour notifications iOS → dépendance Apple inévitable
- **FCM (Google)** : Meilleur protocole Android (vs Huawei HMS, Samsung) - **FCM (Google)** : Protocole standard Android (Google Play Services)
**Alternatives analysées** : **Implémentation directe choisie** :
1. **Custom WebSocket** (self-hosted) : - **Gratuit** : APNS et FCM sont gratuits (pas de limite de volume)
- ✅ Zéro dépendance externe - **Self-hosted** : Code backend 100% maîtrisé, pas de dépendance SDK tiers
- ❌ 150+ heures dev (2-3 sprints) - **Fiabilité** : Infrastructure Apple/Google avec 99.95% uptime
- ❌ Maintien de la reliability en-house - **Batterie** : Utilise les mécanismes système natifs
- ❌ Toujours besoin d'appeler APNS/FCM de toute façon - **Souveraineté** : Aucun vendor lock-in, appels directs aux APIs
- **Simplicité** : HTTP/2 pour APNS, HTTP pour FCM
2. **Novu (open source self-hosted)** : **Alternatives rejetées** :
- ✅ Self-hostable 1. **Firebase SDK** :
- ❌ Jeune (moins mature) - ❌ Vendor lock-in Google
- ❌ Toujours wrapper autour APNS/FCM - ❌ Dépendance SDK externe
- ❌ Contradictoire avec ADR-008 (self-hosted) et ADR-015 (souveraineté)
- ⚠️ Pas d'avantage technique par rapport aux APIs directes
2. **OneSignal / Pusher** :
- ❌ Vendor lock-in + coût élevé (500€+/mois @ 100K users)
- ❌ Abstraction inutile par-dessus APNS/FCM
3. **Novu (open source)** :
- ❌ Overhead sans gain réel - ❌ Overhead sans gain réel
- ❌ Toujours wrapper autour APNS/FCM
3. **OneSignal / Pusher** : **Décision technique** :
- ❌ Même vendor lock-in que Firebase - Implémentation directe APNS/FCM dès le MVP
- ❌ Plus cher (500€+/mois @ 100K users) - **Cohérence ADR** : Respecte ADR-008 (self-hosted) et ADR-015 (souveraineté française)
- **Abstraction layer** : Interface `NotificationProvider` pour faciliter maintenance
**Décision pragmatique** : - **Complexité** : Gestion des certificats APNS + JWT FCM (standard backend)
- Firebase pour MVP : gratuit + fiabilité + time-to-market
- **Mitigation vendor lock-in** : Utiliser abstraction layer (`NotificationProvider` interface)
- **Exit path documenté** : Migration vers custom solution < 1 sprint si besoin futur
- **Probabilité de changement** : Très basse (MVP gratuit, pas d'incitation financière)
### Pourquoi limiter le geofencing local à Phase 2 ? ### Pourquoi limiter le geofencing local à Phase 2 ?
@@ -150,12 +147,12 @@ Architecture hybride en **2 phases** :
### Négatives ### Négatives
- ⚠️ **Dépendance Google (Firebase)** : Contradictoire avec ADR-008 (self-hosted) + ADR-015 (souveraineté FR) - ⚠️ **Gestion certificats APNS** : Renouvellement annuel + configuration
- Mitigé par abstraction layer (`NotificationProvider` interface) → swap facile si besoin - Mitigé par scripts automation (certificats auto-renouvelés)
- Exit path documenté pour migration custom (< 1 sprint) - Documentation complète du processus
- ⚠️ **Données utilisateur chez Google** : Tokens FCM, timestamps notifications - ⚠️ **Tokens push sensibles** : Tokens FCM/APNS stockés côté backend
- Risque RGPD : Nécessite DPA Google valide - Chiffrement tokens en base (conformité RGPD)
- À consulter avec DPO avant déploiement production - Rotation automatique des tokens expirés
- ❌ WebSocket nécessite maintien de connexion (charge serveur +10-20%) - ❌ WebSocket nécessite maintien de connexion (charge serveur +10-20%)
- ❌ Mode offline non disponible au MVP (déception possible des early adopters) - ❌ Mode offline non disponible au MVP (déception possible des early adopters)
@@ -164,58 +161,100 @@ Architecture hybride en **2 phases** :
- **ADR-002 (Streaming)** : Aucun conflit - HLS reste pour l'audio - **ADR-002 (Streaming)** : Aucun conflit - HLS reste pour l'audio
- **ADR-005 (Base de données)** : Ajouter index PostGIS `GIST (geom)` sur `points_of_interest` - **ADR-005 (Base de données)** : Ajouter index PostGIS `GIST (geom)` sur `points_of_interest`
- **ADR-010 (Architecture Backend)** : Ajouter un module `geofencing` avec worker dédié - **ADR-010 (Architecture Backend)** : Ajouter un module `geofencing` avec worker dédié
- **ADR-010 (Frontend Mobile)** : Intégrer `firebase_messaging` (Flutter) et gérer permissions - **ADR-010 (Frontend Mobile)** : Intégrer plugins APNS/FCM natifs (Flutter) et gérer permissions
## Abstraction Layer (Mitigation Vendor Lock-in) ## Abstraction Layer (Maintenabilité)
Pour minimiser le coût de changement future, implémenter une interface abstraite : Implémentation d'une interface abstraite pour gérer APNS et FCM de manière unifiée :
```go ```go
// backend/internal/notification/provider.go // backend/internal/notification/provider.go
type NotificationProvider interface { type NotificationProvider interface {
SendNotification(ctx context.Context, token, title, body, deepLink string) error SendNotification(ctx context.Context, platform, token, title, body, deepLink string) error
UpdateToken(ctx context.Context, userID, newToken string) error UpdateToken(ctx context.Context, userID, platform, newToken string) error
} }
// backend/internal/notification/firebase_provider.go // backend/internal/notification/apns_provider.go
type FirebaseProvider struct { type APNSProvider struct {
client *messaging.Client client *apns2.Client
bundleID string
} }
func (p *FirebaseProvider) SendNotification(ctx context.Context, token, title, body, deepLink string) error { func (p *APNSProvider) SendNotification(ctx context.Context, platform, token, title, body, deepLink string) error {
message := &messaging.Message{ if platform != "ios" {
Notification: &messaging.Notification{ return nil // Not applicable
Title: title,
Body: body,
},
Data: map[string]string{
"deepLink": deepLink,
},
Token: token,
} }
_, err := p.client.Send(ctx, message)
notification := &apns2.Notification{
DeviceToken: token,
Topic: p.bundleID,
Payload: payload.NewPayload().
AlertTitle(title).
AlertBody(body).
Custom("deepLink", deepLink),
}
res, err := p.client.Push(notification)
if err != nil {
return err
}
if res.StatusCode != 200 {
return fmt.Errorf("APNS error: %s", res.Reason)
}
return nil
}
// backend/internal/notification/fcm_provider.go
type FCMProvider struct {
projectID string
client *http.Client
}
func (p *FCMProvider) SendNotification(ctx context.Context, platform, token, title, body, deepLink string) error {
if platform != "android" {
return nil // Not applicable
}
message := map[string]interface{}{
"message": map[string]interface{}{
"token": token,
"notification": map[string]string{
"title": title,
"body": body,
},
"data": map[string]string{
"deepLink": deepLink,
},
},
}
// Call FCM HTTP v1 API
url := fmt.Sprintf("https://fcm.googleapis.com/v1/projects/%s/messages:send", p.projectID)
// ... HTTP POST with OAuth2 token
return err return err
} }
// backend/internal/notification/service.go // backend/internal/notification/service.go
type NotificationService struct { type NotificationService struct {
provider NotificationProvider // ← Interface, pas concrète apnsProvider NotificationProvider
repo NotificationRepository fcmProvider NotificationProvider
repo NotificationRepository
}
func (s *NotificationService) SendPush(ctx context.Context, userID, title, body, deepLink string) error {
user, err := s.repo.GetUser(ctx, userID)
if err != nil {
return err
}
// Route to appropriate provider based on platform
if user.Platform == "ios" {
return s.apnsProvider.SendNotification(ctx, "ios", user.PushToken, title, body, deepLink)
}
return s.fcmProvider.SendNotification(ctx, "android", user.PushToken, title, body, deepLink)
} }
``` ```
**Bénéfice** : Swap Firebase → Custom/Novu sans changer business logic. **Bénéfice** : Code modulaire, testable, et facile à maintenir. Ajout futur de providers alternatifs simple.
```go
// Futur : switch facilement
var provider NotificationProvider
if config.Provider == "firebase" {
provider = &FirebaseProvider{...}
} else if config.Provider == "custom" {
provider = &CustomProvider{...}
}
```
## Métriques de Succès ## Métriques de Succès
@@ -229,8 +268,9 @@ if config.Provider == "firebase" {
### Phase 1 (MVP - Sprint 3-4) ### Phase 1 (MVP - Sprint 3-4)
1. Backend : Implémenter WebSocket endpoint `/ws/location` 1. Backend : Implémenter WebSocket endpoint `/ws/location`
2. Backend : Worker PostGIS avec requête ST_DWithin 2. Backend : Worker PostGIS avec requête ST_DWithin
3. Mobile : Intégrer Firebase SDK + gestion FCM token 3. Backend : Configuration APNS (certificats .p8) + FCM (OAuth2)
4. Test : Validation en conditions réelles (Paris, 10 testeurs) 4. Mobile : Intégrer plugins natifs APNS/FCM + gestion push tokens
5. Test : Validation en conditions réelles (Paris, 10 testeurs)
### Phase 2 (Post-MVP - Sprint 8-10) ### Phase 2 (Post-MVP - Sprint 8-10)
1. Mobile : Implémenter geofencing avec `flutter_background_geolocation` 1. Mobile : Implémenter geofencing avec `flutter_background_geolocation`
@@ -240,7 +280,8 @@ if config.Provider == "firebase" {
## Références ## Références
- [Firebase Cloud Messaging Documentation](https://firebase.google.com/docs/cloud-messaging) - [Apple Push Notification Service (APNS) Documentation](https://developer.apple.com/documentation/usernotifications)
- [Firebase Cloud Messaging HTTP v1 API](https://firebase.google.com/docs/cloud-messaging/http-server-ref)
- [PostGIS ST_DWithin Performance](https://postgis.net/docs/ST_DWithin.html) - [PostGIS ST_DWithin Performance](https://postgis.net/docs/ST_DWithin.html)
- [iOS Geofencing Best Practices](https://developer.apple.com/documentation/corelocation/monitoring_the_user_s_proximity_to_geographic_regions) - [iOS Geofencing Best Practices](https://developer.apple.com/documentation/corelocation/monitoring_the_user_s_proximity_to_geographic_regions)
- Règle Métier 05 : Section 5.1.2 (Mode Piéton, lignes 86-120) - Règle Métier 05 : Section 5.1.2 (Mode Piéton, lignes 86-120)

View File

@@ -41,7 +41,8 @@ Utilisation de **16 librairies open-source** avec licences permissives.
| **Auth JWT** | `zitadel/zitadel-go/v3` | Apache-2.0 | SDK Zitadel officiel (ADR-008) | | **Auth JWT** | `zitadel/zitadel-go/v3` | Apache-2.0 | SDK Zitadel officiel (ADR-008) |
| **WebRTC** | `pion/webrtc/v4` | MIT | Pure Go, radio live (ADR-002) | | **WebRTC** | `pion/webrtc/v4` | MIT | Pure Go, radio live (ADR-002) |
| **WebSocket** | `coder/websocket` | ISC | Minimal, notifications (ADR-017) | | **WebSocket** | `coder/websocket` | ISC | Minimal, notifications (ADR-017) |
| **FCM Push** | `firebase.google.com/go` | BSD-3 | SDK Google officiel (ADR-017) | | **APNS Push** | `sideshow/apns2` | MIT | Client APNS HTTP/2 natif (ADR-017) |
| **FCM Push** | `golang.org/x/oauth2` + HTTP | BSD-3 | FCM HTTP v1 API directe (ADR-017) |
| **HLS/FFmpeg** | `asticode/go-astiav` | MIT | Bindings FFmpeg n8.0 | | **HLS/FFmpeg** | `asticode/go-astiav` | MIT | Bindings FFmpeg n8.0 |
### Utilitaires ### Utilitaires
@@ -88,7 +89,7 @@ Voir [analyse détaillée](../ANALYSE_LIBRAIRIES_GO.md) pour comparatifs complet
### Négatives ### Négatives
- ⚠️ **k6 (AGPL-3.0)** : Copyleft, mais OK pour tests internes (pas de SaaS k6 prévu) - ⚠️ **k6 (AGPL-3.0)** : Copyleft, mais OK pour tests internes (pas de SaaS k6 prévu)
- ⚠️ **Firebase FCM** : Dépendance Google (mitigation via abstraction layer, ADR-017) - ⚠️ **Gestion certificats APNS** : Renouvellement annuel, configuration manuelle
- ❌ Courbe d'apprentissage : 16 librairies à maîtriser (doc nécessaire) - ❌ Courbe d'apprentissage : 16 librairies à maîtriser (doc nécessaire)
### Dépendances go.mod ### Dépendances go.mod
@@ -106,7 +107,8 @@ require (
github.com/zitadel/zitadel-go/v3 latest github.com/zitadel/zitadel-go/v3 latest
github.com/pion/webrtc/v4 latest github.com/pion/webrtc/v4 latest
github.com/coder/websocket latest github.com/coder/websocket latest
firebase.google.com/go/v4 latest github.com/sideshow/apns2 latest
golang.org/x/oauth2 latest // For FCM authentication
github.com/asticode/go-astiav latest github.com/asticode/go-astiav latest
github.com/spf13/viper latest github.com/spf13/viper latest
github.com/rs/zerolog latest github.com/rs/zerolog latest

View File

@@ -31,7 +31,7 @@ Utilisation de **9 librairies open-source** Flutter avec licences permissives, d
| Catégorie | Librairie | Licence | Justification | | Catégorie | Librairie | Licence | Justification |
|-----------|-----------|---------|---------------| |-----------|-----------|---------|---------------|
| **GPS temps réel** | `geolocator` | MIT | Mode voiture, WebSocket position updates, high accuracy | | **GPS temps réel** | `geolocator` | MIT | Mode voiture, WebSocket position updates, high accuracy |
| **Push notifications** | `firebase_messaging` | BSD-3 | FCM tokens, notifications serveur (ADR-017) | | **Push APNS/FCM** | `flutter_apns` + `flutter_fcm` | MIT | Intégration native APNS et FCM directe (ADR-017) |
| **Notifications locales** | `flutter_local_notifications` | BSD-3 | Compteur dynamique, icônes custom, iOS/Android | | **Notifications locales** | `flutter_local_notifications` | BSD-3 | Compteur dynamique, icônes custom, iOS/Android |
| **Permissions** | `permission_handler` | MIT | Gestion unifiée permissions iOS/Android | | **Permissions** | `permission_handler` | MIT | Gestion unifiée permissions iOS/Android |
@@ -69,9 +69,10 @@ Utilisation de **9 librairies open-source** Flutter avec licences permissives, d
- **background_location** : Spécifique background uniquement - **background_location** : Spécifique background uniquement
### Notifications Push ### Notifications Push
- **firebase_messaging** (choisi) : Gratuit, 99.95% uptime, intégration native iOS/Android - **flutter_apns + flutter_fcm** (choisi) : Implémentation directe APNS/FCM, pas de vendor lock-in
- **OneSignal** : Plus cher (500€/mois @ 100K users) - **firebase_messaging** : SDK Firebase, vendor lock-in Google
- **Custom WebSocket** : Complex, toujours besoin APNS/FCM au final (voir ADR-017) - **OneSignal** : Plus cher (500€/mois @ 100K users), vendor lock-in
- **Custom WebSocket** : Complexe, toujours besoin APNS/FCM au final (voir ADR-017)
### Geofencing (Phase 2) ### Geofencing (Phase 2)
- **geofence_service** (choisi) : Natif iOS/Android, économie batterie optimale - **geofence_service** (choisi) : Natif iOS/Android, économie batterie optimale
@@ -95,7 +96,7 @@ Utilisation de **9 librairies open-source** Flutter avec licences permissives, d
- **Compilation native** : Dart → ARM64 (pas de bridge JS comme React Native) - **Compilation native** : Dart → ARM64 (pas de bridge JS comme React Native)
- **just_audio** : Utilise AVPlayer (iOS) et ExoPlayer (Android) natifs - **just_audio** : Utilise AVPlayer (iOS) et ExoPlayer (Android) natifs
- **geolocator** : Accès direct CoreLocation (iOS) et FusedLocation (Android) - **geolocator** : Accès direct CoreLocation (iOS) et FusedLocation (Android)
- **firebase_messaging** : Utilise services systèmes (Google Play Services, APNS) - **flutter_apns + flutter_fcm** : Utilise services systèmes natifs (APNS, Google Play Services)
- **geofence_service** (Phase 2) : Geofencing natif, minimise consommation batterie - **geofence_service** (Phase 2) : Geofencing natif, minimise consommation batterie
### Conformité Stores ### Conformité Stores
@@ -122,7 +123,7 @@ graph TB
subgraph Services["Services Layer - Phase 1 MVP"] subgraph Services["Services Layer - Phase 1 MVP"]
Audio["just_audio<br/>(HLS Streaming)"] Audio["just_audio<br/>(HLS Streaming)"]
GPS["geolocator<br/>(GPS + WebSocket)"] GPS["geolocator<br/>(GPS + WebSocket)"]
FCM["firebase_messaging<br/>(Push Serveur)"] Push["flutter_apns + flutter_fcm<br/>(Push Natifs APNS/FCM)"]
Notif["flutter_local_notifications<br/>(Notifications Locales)"] Notif["flutter_local_notifications<br/>(Notifications Locales)"]
Perms["permission_handler<br/>(Permissions iOS/Android)"] Perms["permission_handler<br/>(Permissions iOS/Android)"]
end end
@@ -140,14 +141,14 @@ graph TB
Bloc --> API Bloc --> API
Bloc --> Audio Bloc --> Audio
Bloc --> GPS Bloc --> GPS
Bloc --> FCM Bloc --> Push
API --> Storage API --> Storage
Widgets --> Cache Widgets --> Cache
GPS --> Perms GPS --> Perms
FCM --> Perms Push --> Perms
FCM --> Notif Push --> Notif
Geofence -.->|Phase 2| Perms Geofence -.->|Phase 2| Perms
Geofence -.->|Phase 2| Notif Geofence -.->|Phase 2| Notif
@@ -179,7 +180,7 @@ graph TB
### Négatives ### Négatives
- ⚠️ **CarPlay/Android Auto** : Packages communautaires (pas officiels Flutter) - ⚠️ **CarPlay/Android Auto** : Packages communautaires (pas officiels Flutter)
- ⚠️ **Firebase dépendance** : Vendor lock-in Google (mitigé par abstraction layer, voir ADR-017) - ⚠️ **Configuration APNS/FCM** : Gestion certificats et OAuth2, configuration manuelle
- ⚠️ **Permission "Always" Phase 2** : Taux acceptation ~30% (geofencing local) - ⚠️ **Permission "Always" Phase 2** : Taux acceptation ~30% (geofencing local)
-**Courbe d'apprentissage** : Dart + pattern BLoC à maîtriser -**Courbe d'apprentissage** : Dart + pattern BLoC à maîtriser
-**Tests stores** : Validation TestFlight (iOS) et Internal Testing (Android) obligatoires -**Tests stores** : Validation TestFlight (iOS) et Internal Testing (Android) obligatoires
@@ -197,7 +198,8 @@ graph TB
**Géolocalisation & Notifications (Phase 1 MVP)** : **Géolocalisation & Notifications (Phase 1 MVP)** :
- `geolocator` - GPS haute précision, WebSocket position updates - `geolocator` - GPS haute précision, WebSocket position updates
- `firebase_messaging` - Push notifications serveur (ADR-017) - `flutter_apns` - Push notifications APNS natif iOS (ADR-017)
- `flutter_fcm` - Push notifications FCM natif Android (ADR-017)
- `flutter_local_notifications` - Notifications locales - `flutter_local_notifications` - Notifications locales
- `permission_handler` - Gestion permissions - `permission_handler` - Gestion permissions
@@ -237,7 +239,8 @@ La section "Packages clés" de l'ADR-010 est désormais obsolète et doit réfé
- [flutter_bloc documentation](https://bloclibrary.dev/) - [flutter_bloc documentation](https://bloclibrary.dev/)
- [just_audio repository](https://pub.dev/packages/just_audio) - [just_audio repository](https://pub.dev/packages/just_audio)
- [geolocator documentation](https://pub.dev/packages/geolocator) - [geolocator documentation](https://pub.dev/packages/geolocator)
- [firebase_messaging documentation](https://pub.dev/packages/firebase_messaging) - [flutter_apns documentation](https://pub.dev/packages/flutter_apns)
- [flutter_fcm documentation](https://pub.dev/packages/flutter_fcm)
- [geofence_service documentation](https://pub.dev/packages/geofence_service) - [geofence_service documentation](https://pub.dev/packages/geofence_service)
- [Apple CarPlay Developer Guide](https://developer.apple.com/carplay/) - [Apple CarPlay Developer Guide](https://developer.apple.com/carplay/)
- [Android Auto Developer Guide](https://developer.android.com/training/cars) - [Android Auto Developer Guide](https://developer.android.com/training/cars)

View File

@@ -133,7 +133,7 @@ Tap pour explorer
- **Engagement piéton** : push actifs pour audio-guides (valeur ajoutée tourisme) - **Engagement piéton** : push actifs pour audio-guides (valeur ajoutée tourisme)
- **Pas de spam** : limite 10/jour + mode silencieux - **Pas de spam** : limite 10/jour + mode silencieux
- **Filtrage géo** : pertinence maximale (pas de notif inutiles) - **Filtrage géo** : pertinence maximale (pas de notif inutiles)
- **Coût** : Firebase Cloud Messaging (gratuit jusqu'à volume élevé) - **Coût** : APNS/FCM natifs (gratuit, aucune limite)
--- ---

View File

@@ -187,7 +187,7 @@ L'équipe RoadWave
**Coût** : **Coût** :
- Email : ~0.001€/notification (Brevo, Resend) - Email : ~0.001€/notification (Brevo, Resend)
- Push : 0€ (Firebase Cloud Messaging / APNs) - Push : 0€ (APNS / FCM natifs)
- In-app : 0€ - In-app : 0€
**Justification** : **Justification** :