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:
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -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** :
|
||||||
|
|||||||
Reference in New Issue
Block a user