Pourquoi Microservices avec Rails ?
Au quotidien, en tant que développeur, vous êtes confronté à des projets de plus en plus complexes et distribués sur plusieurs systèmes et infrastructures. Les microservices ont été conçus pour aider à gérer ces projets en les décomposant en composants indépendants mais interconnectés. Cela permet d'améliorer la scalabilité, la maintenabilité et la flexibilité du code. Un cas d'usage concret est le développement d'une application e-commerce avec une base de données monolithique qui devient rapidement trop lourde à gérer.
Prerequis
Pour suivre ce tutoriel, vous aurez besoin des compétences suivantes :
- Connaissance approfondie de Ruby et Rails
- Expérience avec les bases de données SQL et NoSQL
- Familiarité avec Docker pour le déploiement
- Compétence en gestion des API RESTful
Vous devrez également installer les outils suivants :
- Ruby 3.2.x (ou ultérieur)
- Rails 7.0.x (ou ultérieur)
- Node.js 16.x (pour les tâches backend et frontend si nécessaire)
- PostgreSQL pour la base de données
- Docker et Docker Compose
Concepts fondamentaux
Architecture des Microservices
Un microservice est un composant indépendant qui fait une seule chose bien. Chaque service a sa propre base de données, son propre langage de programmation et son propre déploiement.
## Un exemple d'un microservice simple avec Rails
class Task < ApplicationRecord
end
class TasksController < ApplicationController
def index
tasks = Task.all
render json: tasks
end
def create
task = Task.create!(task_params)
render json: task, status: :created
end
private
def task_params
params.require(:task).permit(:title, :description)
end
end
Communication Entre Microservices
Les microservices communiquent entre eux via des appels API RESTful ou gRPC. Cela permet une grande flexibilité et évolutivité.
## Un exemple d'appel API pour récupérer les tâches depuis un autre microservice
require 'net/http'
def get_tasks_from_another_service
uri = URI('http://another-service/tasks')
response = Net::HTTP.get(uri)
JSON.parse(response)
end
## Utilisation dans un controller
class TasksController < ApplicationController
def index
tasks = get_tasks_from_another_service
render json: tasks
end
end
Gestion des États et Transactions
Chaque microservice doit gérer son propre état, ce qui signifie que les transactions complexes doivent être orchestrées entre les services.
## Un exemple d'orchestration de transactions entre plusieurs services
class OrderService < ApplicationService
def call(order_params)
begin
order = Order.create!(order_params)
PaymentService.new(order).call
EmailService.new(order).call
order.update(status: 'completed')
rescue StandardError => e
order.update(status: 'failed')
raise e
end
end
end
Mise en pratique : Projet fil rouge
Nous allons créer un mini-projet complet : une application de gestion des tâches avec plusieurs microservices.
Étape 1 : Configuration du projet
Créez un nouveau répertoire et initialisez un nouveau projet Rails.
mkdir task-manager
cd task-manager
rails new backend --api --database=postgresql
Ajoutez Docker à votre projet pour faciliter le déploiement.
## docker-compose.yml
version: '3.8'
services:
db:
image: postgres
environment:
POSTGRES_DB: task_manager_development
POSTGRES_USER: postgres
POSTGRES_PASSWORD: password
volumes:
- ./db/postgresql:/var/lib/postgresql/data
backend:
build: .
ports:
- "3000:3000"
volumes:
- .:/app
depends_on:
- db
Étape 2 : Création des microservices
Créez trois microservices : tasks, users et auth.
rails g service TaskService
rails g model User email:string password_digest:string
rails g controller Users create show destroy
rails g controller Auth login
Étape 3 : Configuration des routes
Configurez les routes pour chaque microservice.
## config/routes.rb
Rails.application.routes.draw do
namespace :tasks do
resources :tasks, only: [:index, :create]
end
namespace :users do
resources :users, only: [:create, :show, :destroy]
end
namespace :auth do
post 'login', to: 'auth#login'
end
end
Étape 4 : Implémentation des microservices
Implémentez les fonctionnalités pour chaque microservice.
## app/services/task_service.rb
class TaskService < ApplicationService
def call(task_params)
task = Task.create!(task_params)
task
end
end
## app/controllers/tasks_controller.rb
class TasksController < ApplicationController
def index
tasks = Task.all
render json: tasks
end
def create
task_service = TaskService.new
task = task_service.call(task_params)
render json: task, status: :created
end
private
def task_params
params.require(:task).permit(:title, :description)
end
end
Étape 5 : Déploiement avec Docker
Déployez votre application avec Docker.
docker-compose up --build
Erreurs frequentes et debugging
Voici trois erreurs courantes et leurs solutions :
Erreur : NoMethodError - undefined method `permit' for #Hash:0x00007f8b9c3a3a40
# Code incorrect params.require(:task).permit(:title, :description) # Code correct task_params = params.require(:task).permit(:title, :description)Erreur : ActiveRecord::RecordInvalid - Validation failed: Title can't be blank
# Code incorrect Task.create!(task_params) # Code correct if task.save render json: task, status: :created else render json: task.errors, status: :unprocessable_entity endErreur : Net::HTTPServerException - 404 "Not Found"
# Code incorrect uri = URI('http://another-service/tasks') response = Net::HTTP.get(uri) # Code correct uri = URI('http://localhost:3001/tasks') # Assurez-vous que le port est correct response = Net::HTTP.get(uri)
Pour aller plus loin
Intégration Continu et Déploiement Automatisé (CI/CD)
Utilisez des outils comme GitHub Actions ou GitLab CI pour automatiser les tests et le déploiement.
Docker Compose pour la gestion des environnements
Utilisez Docker Compose pour gérer les dépendances et les services en un seul fichier.
Gestion des Sécurité et Authentification
Ajoutez une couche de sécurité avec des mécanismes d'authentification et d'autorisation robustes.
Défi pratique : Créez un microservice pour gérer les commentaires des tâches et ajoutez-le à votre application.