Skip to content

gbourgeat/test-seliaris

Repository files navigation

Seliaris Weather API

Service HTTP qui interroge OpenWeatherMap pour retourner la météo courante d'une ville. Trois modes d'entrée mutuellement exclusifs : nom de ville, identifiant OpenWeatherMap, ou coordonnées géographiques.

Stack PHP 8.5 · Symfony 8.0 · FrankenPHP · Docker
Architecture Monolithe modulaire · DDD / Hexagonal · CQRS read-side
Qualité PHPStan level 8 · PHP-CS-Fixer (PSR-12 + Symfony) · Rector · pyramide PHPUnit (62 tests / 107 assertions)
Doc API OpenAPI 3 + Swagger UI sur http://localhost:8000/api/doc

Démarrage rapide

Pré-requis : Docker + Docker Compose.

# 1. Démarrer la stack (FrankenPHP sur :8000)
make up

# 2. Installer les dépendances PHP dans le conteneur
make update

# 3. Renseigner votre clé OpenWeatherMap dans un fichier non versionné
echo 'OPEN_WEATHER_MAP_API_KEY=votre_cle_ici' > .env.local

# 4. Vider le cache pour prendre en compte la nouvelle clé
make cache-clear

C'est prêt :

  • APIhttp://localhost:8000/api/weather/current?city=Paris
  • Swagger UIhttp://localhost:8000/api/doc
  • OpenAPI JSONhttp://localhost:8000/api/doc.json

Pour arrêter la stack : make down.

Utilisation de l'API

L'endpoint GET /api/weather/current accepte exactement un des trois modes d'interrogation :

# Par nom de ville (avec hint pays optionnel)
curl 'http://localhost:8000/api/weather/current?city=Paris&country=fr'

# Par identifiant OpenWeatherMap
curl 'http://localhost:8000/api/weather/current?id=2988507'

# Par coordonnées géographiques
curl 'http://localhost:8000/api/weather/current?lat=48.8566&lon=2.3522'

Réponse 200 (extrait) :

{
  "location":   { "name": "Paris", "country": "FR", "id": 2988507, "latitude": 48.85, "longitude": 2.35 },
  "temperature":{ "value": 17.24, "unit": "celsius" },
  "feels_like": { "value": 16.38, "unit": "celsius" },
  "humidity":   52,
  "pressure":   1015,
  "wind":       { "speed_m_s": 5.66, "direction_deg": 20 },
  "condition":  { "main": "Clear", "description": "ciel dégagé", "icon": "01n" },
  "observed_at":"2026-04-28T21:58:11+00:00"
}

Erreurs (RFC 7807 — application/problem+json) :

Code Cause
400 Aucun critère, critères ambigus, valeur hors plage, type invalide
404 OpenWeatherMap n'a aucune observation pour cette localisation
500 Clé API invalide / non autorisée
502 OpenWeatherMap injoignable ou réponse malformée

Architecture

Monolithe modulaire organisé en bounded contexts indépendants. Chaque module suit la même topologie hexagonale (Domain ← Application ← Infrastructure).

src/
├── Shared/                                # Kernel partagé (cross-cutting)
│   ├── Domain/Exception/                  # DomainException + interfaces marqueurs
│   │                                      # (BadInputProblem, NotFoundProblem, …)
│   └── Infrastructure/Http/EventListener/ # JsonExceptionListener (RFC 7807)
└── Weather/                               # Bounded context "Weather"
    ├── Domain/                            # Pur PHP, zéro dépendance Symfony
    │   ├── ValueObject/                   #   CityName, Coordinates, Temperature, …
    │   ├── Query/                         #   WeatherQuery + 3 impls (sealed-like)
    │   ├── Model/Weather                  #   Agrégat read-only
    │   ├── Port/WeatherProvider           #   Interface (port sortant)
    │   └── Exception/
    ├── Application/                       # Use-cases (CQRS read-side)
    │   ├── Query/GetCurrentWeather + Handler
    │   ├── Factory/WeatherQueryFactory    #   Parsing GET → WeatherQuery
    │   └── DTO/CurrentWeatherView         #   Read-model
    └── Infrastructure/                    # Adaptateurs
        ├── Http/Controller/               #   GetCurrentWeatherController
        └── Provider/OpenWeatherMap/       #   Adapter du port WeatherProvider
            ├── OpenWeatherMapProvider
            └── Mapper/                    #   Payload OWM → Weather aggregate

Choix de conception

  • DIP — Le domaine ne connaît que WeatherProvider. L'alias DI route vers l'adaptateur OpenWeatherMap ; en remplacer un autre ne touche que la config.
  • VOs always-validCityName, Coordinates, Humidity, etc. valident leurs invariants en constructeur. Aucun objet incohérent ne circule.
  • WeatherQuery sealed-like — interface marqueur + 3 final readonly classes ; ajouter un mode d'entrée n'impacte que les adaptateurs concernés.
  • SRP — Controller (HTTP), Factory (parsing), Handler (use-case), Mapper (sérialisation), Listener (erreur → HTTP) : un seul motif de changement par classe.
  • Erreurs typées — chaque exception domaine implémente une interface marqueur (BadInputProblem, NotFoundProblem, UpstreamUnavailableProblem, UpstreamConfigurationProblem). Le JsonExceptionListener les mappe en statuts HTTP sans connaître les modules.

Pyramide de tests

       ┌──────────────┐
       │  Functional  │   6 — endpoint complet via WebTestCase, port stubbé
       ├──────────────┤
       │ Integration  │   8 — provider OWM via Symfony MockHttpClient
       ├──────────────┤
       │     Unit     │  47 — VOs, Factory, Handler, Mapper
       └──────────────┘
make test                  # tout
make test-unit             # uniquement la base de la pyramide
make test-integration      # uniquement le middle tier
make test-functional       # uniquement le sommet (boot du Kernel)

Qualité

make qa          # phpstan + cs + rector + tests
make stan        # PHPStan level 8 (src + tests)
make cs / cs-fix # PHP-CS-Fixer (PSR-12 + Symfony + PHP84)
make rector      # Rector (PHP 8.4 + 7 sets)

Configuration

Variables d'environnement (.env pour les valeurs par défaut, .env.local pour les secrets, jamais versionné) :

Variable Défaut Description
OPEN_WEATHER_MAP_BASE_URI https://api.openweathermap.org URL de base d'OpenWeatherMap
OPEN_WEATHER_MAP_API_KEY changeme Clé API (à overrider dans .env.local)
OPEN_WEATHER_MAP_LANG fr Langue des descriptions
OPEN_WEATHER_MAP_UNITS metric metric (°C / m·s⁻¹), imperial, ou défaut Kelvin
OPEN_WEATHER_MAP_TIMEOUT 5 Timeout HTTP en secondes

Commandes Make utiles

make help            # lister toutes les cibles disponibles
make up / down       # cycle de vie de la stack Docker
make sh              # shell dans le conteneur app
make logs            # tail des logs FrankenPHP
make console c="…"   # exécuter une commande bin/console

About

Symfony 8 / FrankenPHP weather API backed by OpenWeatherMap — hexagonal architecture, CQRS read-side, RFC 7807, OpenAPI 3

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages