Hanami framework alternatif Rails

Hanami framework alternatif Rails : Le successeur moderne de Ruby ?

Tutoriel Ruby

Hanami framework alternatif Rails : Le successeur moderne de Ruby ?

Découvrir le Hanami framework alternatif Rails est devenu un sujet brûlant dans la communauté Ruby. Ce framework réinvente l’approche web en Ruby en adoptant une philosophie de conception plus modulaire, plus explicite et axée sur les entités métier plutôt que sur l’Object-Relational Mapping (ORM) monolithique. Il ne cherche pas simplement à remplacer Ruby on Rails, mais à résoudre ses points de friction historiques pour créer une expérience de développement web plus agréable et plus prédictible.

Contrairement à la perception courante, il ne s’agit pas d’une simple réplique ou d’une baisse de qualité. Au contraire, il capitalise sur les meilleures pratiques modernes pour offrir une structure qui respecte le principe de séparation des préoccupations (Separation of Concerns) de manière stricte. Si vous êtes un développeur habitué à la flexibilité de Rails mais frustré par son caractère « magique » et ses dépendances cachées, comprendre le Hanami framework alternatif Rails est une étape clé dans l’évolution de votre stack technique.

Ce guide approfondi est conçu pour les développeurs Ruby ayant déjà une bonne maîtrise de l’écosystème Rails et cherchant à migrer ou à tester une architecture alternative. Nous allons explorer en profondeur ce qui rend Hanami unique, comment il diffère de son prédécesseur, et dans quels contextes il excelle. Nous allons d’abord décortiquer les concepts théoriques qui fondent Hanami, puis nous présenterons des exemples de code concrets, des cas d’usage avancés, et des meilleures pratiques pour vous aider à démarrer en toute confiance. Préparez-vous à découvrir un paradigme de développement web Ruby qui place l’explicite au centre de votre architecture.

Hanami framework alternatif Rails
Hanami framework alternatif Rails — illustration

🛠️ Prérequis

Pour commencer à explorer le Hanami framework alternatif Rails, quelques prérequis techniques sont nécessaires. Il est crucial d’avoir un environnement Ruby stable et bien configuré pour éviter des problèmes de dépendances. Ne négligez jamais la gestion des versions, car Hanami, bien que moderne, exige un socle solide pour fonctionner efficacement. Voici ce que vous devez préparer :

Prérequis Techniques Détaillés

Connaissances Requises : Une bonne maîtrise du langage Ruby (version 3.0+ recommandée), et une expérience significative avec le développement d’applications web MVC (Model-View-Controller) est fortement recommandée. La compréhension des concepts d’injection de dépendances et de la programmation orientée domaine (Domain Driven Design – DDD) est un atout majeur.

Environnement Nécessaire

  • Version de Ruby : Ruby 3.0 ou supérieur.
  • Gestionnaire de Gemmes : Bundler (pour gérer les dépendances).
  • Outil de Base de Données : SQLite3 ou PostgreSQL (selon le projet).

Commandes d’Installation

Assurez-vous d’installer les dépendances de base et de créer une nouvelle application Hanami. Voici les commandes précises :

gem install bundler
gem install sqlite3
hanami generate app ma_premiere_app

Après cette étape, vous devez naviguer dans le répertoire et exécuter les migrations initiales pour initialiser la base de données. Ces prérequis garantissent un terrain de jeu stable pour appréhender la philosophie du Hanami framework alternatif Rails.

📚 Comprendre Hanami framework alternatif Rails

Comprendre le fonctionnement interne de Hanami nécessite de dépasser la simple comparaison avec Rails. Alors que Rails excelle dans la productivité rapide grâce à sa magie implicite, Hanami mise sur la prévisibilité et l’explicite. C’est le concept de « Composition sur Héritage » qui est central. Il force le développeur à définir clairement les contrats de chaque composant, ce qui est fondamental pour les architectures de grande envergure.

Le cœur de Hanami framework alternatif Rails : Explicite et Modulaire

Hanami structure l’application non pas comme un ensemble monolithique, mais comme un ensemble de services autonomes. Chaque fonctionnalité—une API, un module d’administration, un processus batch—est encapsulée dans son propre composant, minimisant les effets de bord imprévus. Imaginons une application bancaire : plutôt que d’avoir un modèle Account qui gère toutes les interactions (gestion des fonds, logs, etc.), Hanami séparerait ceci en trois services distincts : AccountRepository, FundTransferService, et TransactionLogger.

Cette approche est directement inspirée des patterns d’architecture comme Hexagonal (Ports and Adapters). Au lieu de laisser la couche de persistance (le « quoi » et le « comment ») dicter l’architecture (le « pourquoi »), Hanami force une déconnexion claire. La couche de métier interagit avec des « ports » (des interfaces définies par des services), et des « adaptateurs » (comme un SequelRepository ou un ActiveRecordRepository) implémentent ces ports en utilisant des technologies spécifiques. Cela rend le code beaucoup plus testable et plus facile à faire évoluer.

Hanami vs Rails : Une Analogie de Construction

Si Rails est un système de construction préfabriqué ultra-performant, mais dont les murs cachés peuvent rendre le dépannage complexe, Hanami est plutôt un ensemble de briques modulaires de haute qualité. Chaque brique (un service, un *action object*) est conçue pour une seule tâche et peut être remplacée sans affecter l’ensemble. Ceci est particulièrement visible dans la gestion des dépendances. Un développeur Hanami peut décider, par exemple, de remplacer la base de données PostgreSQL par Redis pour une couche de cache critique, sans modifier la logique métier, car la dépendance est injectée au niveau du service.

Les avantages de ce design sont manifestes en termes de maintenabilité. La séparation des préoccupations est tellement stricte que la traçabilité d’une fonctionnalité est immédiate. L’utilisation d’objets de valeur (Value Objects) et de services clairs (Service Objects) est encouragée, et non pas seulement la dépendance au modèle global. C’est pourquoi, pour les projets nécessitant une évolutivité architecturale maximale, considérer le Hanami framework alternatif Rails est un choix stratégique.

Hanami framework alternatif Rails
Hanami framework alternatif Rails

💎 Le code — Hanami framework alternatif Rails

Ruby
require 'hanami'
# Implémentation d'un simple service de création d'utilisateur,
# illustrant la séparation des préoccupations.

class UserService
  attr_reader :repository # Injection de dépendance

  # Le constructeur reçoit le dépôt (repository) nécessaire
  def initialize(repository: UserRepository.new)
    @repository = repository
  end

  # Méthode principale pour créer un utilisateur
  # @param email [String] L'email de l'utilisateur
  # @param password [String] Le mot de passe en clair
  # @return [Hash] L'utilisateur créé ou nil en cas d'erreur
  def call(email:, password:)
    # 1. Validation des données (Cas limite)
    return nil unless email.to_s.include?('@') && password.length >= 8

    # 2. Vérification d'existence (Cas limite)
    if repository.find_by_email(email)
      puts "Erreur: Cet email est déjà utilisé."
      return nil
    end

    # 3. Traitement métier : Hachage du mot de passe
    hashed_password = BCrypt::Password.create(password)

    # 4. Création et persistance
    user = repository.create(
      email: email,
      password_hash: hashed_password
    )

    puts "Utilisateur créé avec succès ! ID: #{user.id}"
    user
  rescue StandardError => e
    puts "Une erreur de base de données est survenue: #{e.message}"
    nil
  end
end

# --- Simules de dépendances pour l'exécution autonome ---

# Simulation de la librairie de hachage (nécessite la gem 'bcrypt')
module BCrypt
  def self.create(password)
    # Simule un hachage complexe
    "HASH_#{password.upcase}_#{Time.now.to_i}"
  end
end

# Simulation du dépôt (Repository Layer)
class UserRepository
  attr_reader :users # Simule la collection de données

  def initialize
    @users = [] # Simule la base de données
  end

  def find_by_email(email)
    # Simule la recherche en DB
    @users.find { |u| u[:email] == email }
  end

  def create(attributes)
    # Simule l'enregistrement en DB
    new_user = { id: rand(1000), **attributes }
    @users << new_user
    new_user
  end
end

# --- Exemple d'exécution ---
service = UserService.new
service.call(email: "test@exemple.com", password: "SuperMotDePasse123")
service.call(email: "test@exemple.com", password: "motrapasse" ) # Test de limite d'email
service.call(email: "invalide", password: "longenough") # Test de limite d'email

📖 Explication détaillée

L’analyse de ce premier snippet de code est essentielle pour saisir la philosophie du Hanami framework alternatif Rails. Ce code ne montre pas seulement comment créer un utilisateur; il montre comment ce framework vous force à structurer votre code en couches bien définies, ce qui est son atout majeur.

Décryptage du Service Layer et Injection de Dépendances

Le cœur de ce système est la classe UserService. Elle est un objet de service qui orchestre l’opération de création d’utilisateur. Remarquez l’initialisation : def initialize(repository: UserRepository.new). Ici, nous pratiquons l’Injection de Dépendances (DI). Au lieu que UserService crée lui-même un UserRepository, on le reçoit en paramètre. Pourquoi est-ce crucial? Parce que cela signifie que vous pouvez facilement remplacer le UserRepository par un mock pour les tests, ou par un autre type de dépôt (ex: un dépôt NoSQL) sans toucher au code de service. C’est la pureté et la testabilité.

  • Validation (Cas Limite) : La première chose dans call est la validation des entrées. Il y a un retour immédiat de nil si l’email n’est pas valide. Ceci empêche l’exécution des étapes coûteuses (comme le hachage ou l’appel DB) avec des données corrompues.
  • Sécurité : Le mot de passe n’est jamais stocké en clair. Il est haché via BCrypt::Password.create. Bien que ceci soit une bonne pratique générale, l’utilisation d’un module simulé montre que chaque dépendance sécuritaire doit être gérée explicitement.
  • Persistance : La méthode repository.create est l’interaction avec la base de données. Elle est isolée. Si nous devions passer de SQLite à Postgres, seul ce dépôt devrait changer, la logique de Hanami framework alternatif Rails restant intacte.

Le bloc rescue StandardError => e est également fondamental. Il agit comme un filet de sécurité autour des opérations critiques, capturant les erreurs de bas niveau (connexion DB, timeout) et permettant au service de retourner un résultat géré plutôt que de planter l’application entière. C’est un pattern de gestion des erreurs qui renforce la robustesse, une caractéristique que les développeurs qui migrent vers le Hanami framework alternatif Rails apprécient énormément par rapport aux mécanismes d’exception parfois implicites d’autres frameworks.

🔄 Second exemple — Hanami framework alternatif Rails

Ruby
require "hanami/model"

# Implémentation d'une entité complexe (Payment) utilisant l'injection de dépendance
# pour gérer plusieurs étapes métier.

class PaymentService
  def initialize(gateway:, logger:)
    @gateway = gateway # Interface de paiement (ex: StripeClient)
    @logger = logger   # Logger de transactions
  end

  # @param amount [Float] Montant à payer
  # @param token [String] Token de paiement client
  def charge(amount:, token:)
    # 1. Validation du montant
    raise ArgumentError, "Le montant doit être positif." if amount <= 0

    begin
      # 2. Appel externe (Interface de Paiement)
      success = @gateway.process_payment(token: token, amount: amount)

      unless success
        @logger.log(status: :failed, reason: :invalid_token)
        raise StandardError, "Échec du paiement: Token invalide."
      end

      # 3. Persistance et Logique post-paiement
      transaction = { id: rand(5000), amount: amount, status: :paid }
      @logger.log(status: :success, transaction_id: transaction[:id])

      return transaction
    rescue ArgumentError => e
      puts "Erreur: #{e.message}"
      return nil
    rescue StandardError => e
      puts "Erreur critique: #{e.message}"
      return nil
    end
  end
end

# --- Simules de dépendances ---
class StripeClient
  def self.process_payment(token:, amount:)
    # Simule un appel API externe
    token.start_with?('tok_')
  end
end

class ConsoleLogger
  def log(data)
    puts "[LOG] Transaction enregistrée: #{data}"
  end
end

# --- Exemple d'utilisation ---
gateway_client = StripeClient
logger_instance = ConsoleLogger.new
payment_service = PaymentService.new(gateway: gateway_client, logger: logger_instance)

# Cas réussi
puts "--- Tentative de paiement réussie ---"
payment_service.charge(amount: 49.99, token: 'tok_abc123')

# Cas échoué (mauvais token)
puts "\n--- Tentative de paiement échouée (token) ---"
payment_service.charge(amount: 10.00, token: 'bad_token')

# Cas limite (argument invalide)
puts "\n--- Test de validation de montant ---"
payment_service.charge(amount: 0, token: 'tok_abc123')

▶️ Exemple d’utilisation

Imaginons un scénario où nous devons gérer la commande d’un article en ligne, nécessitant validation, calcul d’impôts et notification. Ce processus complexe est parfait pour démontrer l’efficacité du pattern de service propre à un Hanami framework alternatif Rails. Nous allons créer une séquence d’actions :

1. Récupérer l’article (Repository).

2. Appliquer la logique tarifaire (Service). La logique doit déterminer si une taxe spéciale s’applique en fonction de la zone.

3. Enregistrer la transaction (Repository). Ceci garantit l’atomicité.

Simulation de l’exécution :


# Simulation de la Gem de Taxe
module TaxCalculator
  def self.calculate(price, zone)
    return price * 1.20 if zone == :eu # Taxe de 20%
    price * 1.05
  end
end

# Le service orchestre le flux
class OrderService
  def self.place_order(article_id:, zone:)
    article = ArticleRepository.find(article_id)
    taxable_amount = TaxCalculator.calculate(article.price, zone)

    # Simulation de la création de la commande
    order = OrderRepository.create(
      article_id: article_id,
      total_amount: taxable_amount,
      status: :pending
    )
    puts "Commande #{order.id} créée avec un total de #{taxable_amount.round(2)}."

    # Simulation de la notification
    NotificationService.send_confirmation(order.user_email)
    puts "Confirmation envoyée à #{order.user_email}."
  end
end

# Appel
OrderService.place_order(article_id: 42, zone: :eu)

Analyse de la sortie attendue :

La première ligne confirme la création de la commande. Cela prouve que toutes les étapes—récupération de l’article, calcul fiscal, et enregistrement—ont été effectuées séquentiellement dans un seul bloc transactionnel. La seconde ligne montre l’exécution du service de notification. L’utilisation de classes et de modules séparés (comme TaxCalculator et OrderService) montre que le framework favorise une architecture où chaque responsabilité est confinée, améliorant la lisibilité et la testabilité du code beaucoup plus qu’un simple modèle monolithique.

🚀 Cas d’usage avancés

Le véritable pouvoir de Hanami framework alternatif Rails se révèle dans la gestion de cas d’usage complexes, où la séparation des préoccupations est vitale. Voici quatre exemples avancés qui illustrent cette force architecturale, allant au-delà du simple CRUD.

1. Workflow de Mise à Jour de Statut (State Machine)

Dans un CMS, l’état d’un article passe de ‘Brouillon’ à ‘En Revue’ puis ‘Publié’. Plutôt que d’ajouter une logique conditionnelle dans le modèle, on utilise un service dédié. Ce service valide la séquence et appelle la persistance dans l’ordre. Exemple de code : class ArticleWorkflowService; def update_status(article:, new_status: :published); raise InvalidStateError, "Impossible de publier un article en cours de modération." unless article.review? ArticleRepository.update(article.id, status: new_status); end; end

2. Génération de Rapports Financiers Asynchrones

Les rapports lourds ne doivent jamais bloquer l’API. Hanami encourage l’utilisation de queues de messages. Un service envoie la requête à un job manager, et un worker séparé exécute la tâche. Ceci est crucial pour la scalabilité. ReportJob.perform_async(user_id: 123, start_date: '2024-01-01'); # L'API répond immédiatement avec un job_id.

3. Intégration d’API Tierces (OAuth/Payments)

Lors de l’intégration d’un service de paiement comme Stripe, l’interaction doit être isolée. Le service de paiement est le seul composant qui connaît les détails du client externe (API keys, formats de requête). class PaymentProcessor; def charge(user_id:, amount:); payment_client = Stripe::Client.new(api_key: ENV['STRIPE_KEY']); payment_client.charge(user_id, amount); end; end

4. Moteur de Règles Métier (Rule Engine)

Pour déterminer si un utilisateur a droit à une remise, on n’utilise pas un simple if/else. On passe par un moteur de règles (ex: CanCanCan ou un service dédié). Le service reçoit le contexte et exécute les règles dans un ordre prédéfini. class DiscountService; def check_discount(user:, cart:); rules = [UserRules, LocationRules, BulkBuyRules]; rules.each { |rule| rule.run(user, cart) }; end; end

⚠️ Erreurs courantes à éviter

La transition vers Hanami framework alternatif Rails peut comporter des pièges méthodologiques. Les développeurs venant de l’écosystème Rails peuvent être tentés de faire ce qu’on appelle un « Mass Assignment

✔️ Bonnes pratiques

Adopter Hanami framework alternatif Rails demande de se conformer à un certain set de bonnes pratiques d’architecture pour en tirer tout le bénéfice. Suivre ces conventions garantira un code propre, maintenable et résilient à l’évolution des besoins métier. Voici cinq conseils professionnels incontournables :

  • Principes de DDD (Domain Driven Design) : Chaque entité métier doit être modélisée avec un service (un « use case »). Ne jamais modifier directement un objet de données (Repository) sans passer par un service d’orchestration. Cela force la pensée orientée domaine.
  • Minimalisme du Dépôt (Repository) : Le rôle du Repository est *uniquement* de mapper les objets métier aux données persistantes et vice-versa. Il ne doit contenir aucune logique métier de validation ou de calcul.
  • Utilisation systématique de DTOs (Data Transfer Objects) : Lorsque vous passez des données entre services, n’utilisez pas les objets de modèle bruts. Créez des DTOs. Cela garantit que le contrat de données est maintenu, même si le modèle de base de données évolue.
  • Gestion des Erreurs Explicite : Ne pas utiliser de StandardError généraliste. Définissez des erreurs métier spécifiques (ex: InvalidStateError, InsufficientFundsError). Cela permet au code appelant de gérer l’échec de manière granulaire.
  • Injection de Dépendances Partout : Même pour les classes les plus petites, injectez les dépendances au lieu de les instancier localement. Ceci est la fondation d’un système testable et de haute qualité, pierre angulaire de tout Hanami framework alternatif Rails réussi.
📌 Points clés à retenir

  • Séparation stricte des préoccupations : Hanami sépare les actions métier, la logique de service et la persistance en couches distinctes.
  • Approche explicite : Contrairement à la magie de Rails, Hanami rend chaque étape du processus visible, améliorant la traçabilité du code.
  • Injection de dépendances : Ce pattern est encouragé systématiquement, assurant que les composants sont interchangeables et facilement testables.
  • Focus sur les Repositories : La couche Repository est le gardien de la persistance, limitant l'accès aux données et empêchant la pollution du modèle.
  • Architecture Modulaire : Le framework encourage la division en modules (micro-services légers), idéal pour les grandes applications complexes.
  • Robustesse contre l'évolution : Grâce à sa conception modulaire, les mises à jour et l'ajout de fonctionnalités sont moins susceptibles de casser des parties éloignées du système.
  • Performance et Prédictibilité : Le code est souvent plus prédictible et plus performant dans les cas d'usage très spécifiques, grâce à sa clarté structurelle.
  • Idéal pour les grands systèmes : Si le projet est censé grandir et changer de modules régulièrement, le choix d'un Hanami framework alternatif Rails est une assurance qualité architecturale.

✅ Conclusion

En conclusion, le Hanami framework alternatif Rails représente une évolution majeure dans le paradigme du développement web Ruby. Nous avons vu qu’il ne s’agit pas d’une simple alternative, mais d’une réaffirmation des principes d’excellence architecturale : explicité, modularité, et séparation des préoccupations. Si le développement sur Rails vous a permis d’être productif rapidement, Hanami vous force à écrire du code qui sera, par nature, plus robuste, plus maintenable et, ultimement, plus apte à évoluer sur le long terme. Les cas d’usage avancés, tels que la gestion des workflows complexes ou l’intégration d’API tierces, illustrent parfaitement comment son architecture favorise des interactions propres et testables. L’investissement initial en temps pour comprendre le DDD et l’injection de dépendances paie des dividendes énormes en termes de qualité logicielle.

Pour approfondir, nous vous recommandons d’étudier des patterns comme Hexagonal Architecture et de vous plonger dans les discussions sur le Domain Driven Design. La documentation officielle sur les guides de Hanami est une mine d’or https://hanami.conjur.io/. Un excellent exercice pratique serait de refactoriser un simple CRUD de votre ancien projet Rails en utilisant les services Hanami pour chaque action. Souvenez-vous de l’anecdote de la communauté : un grand système qui fonctionne parfaitement aujourd’hui, mais que personne n’ose modifier par peur de casser quelque chose, est un système qui souffre d’un manque de clarté architecturale. Hanami résout ce problème.

Pour conclure, maîtriser le Hanami framework alternatif Rails vous positionne non seulement comme un expert Ruby, mais comme un architecte logiciel sensible aux meilleures pratiques modernes. N’hésitez plus. Commencez votre projet pilote dès aujourd’hui et faites l’expérience de la puissance du code explicite !

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *