Skip to content

Les bases à connaître ​

Cette page explique les notions dont le reste de la documentation se sert, avec des exemples tirés du code de l'API. Pas besoin de tout retenir : reviens-y quand un mot t'arrête.

Elixir, le langage ​

Elixir est un langage fonctionnel qui tourne sur la machine virtuelle d'Erlang (la BEAM), conçue pour les systèmes qui ne doivent jamais s'arrêter, comme les centraux téléphoniques.

Les données ne changent jamais ​

Une valeur n'est jamais modifiée sur place : une fonction renvoie une nouvelle valeur. Il n'y a donc pas d'effet de bord caché : ce qu'une fonction reçoit, elle ne peut pas l'abîmer.

Le pattern matching ​

= ne veut pas dire « affecter » mais « faire correspondre un motif ». C'est l'outil le plus utilisé du projet. Par exemple, la règle qui dit que personne ne pointe pour un autre (authorization.ex) :

elixir
defp allowed?(%User{id: id}, :clock, id), do: true

La variable id apparaît deux fois : la clause ne s'applique que si l'id de la personne connectée est égal à celui de la personne qui pointe. Toutes les autres combinaisons tombent dans une clause qui refuse.

Le pipe |> ​

a |> f(b) veut dire f(a, b). On écrit une suite de transformations dans l'ordre où on la lit :

elixir
first_day = today() |> Date.add(-1) |> Date.beginning_of_week() |> Date.add(-21)

« Aujourd'hui, moins un jour, le lundi de cette semaine, moins trois semaines. »

with : une suite d'étapes qui peuvent échouer ​

Chaque ligne motif <- expression doit correspondre à son motif pour passer à la suivante. Dès qu'une étape renvoie autre chose (souvent {:error, raison}), le with s'arrête et renvoie cette valeur. C'est la forme de presque tous les contrôleurs :

elixir
with {:ok, user_id} <- parse_id(user_id),
     :ok <- Authorization.authorize(conn.assigns.current_user, :clock, user_id),
     {:ok, clock, result} <- Clocks.clock(user_id, params) do
  render(conn, :show, clock: clock)
end

{:ok, valeur} et {:error, raison} ​

Une fonction qui peut échouer ne lève pas d'exception : elle renvoie un tuple qui dit comment ça s'est passé. Celui qui l'appelle est obligé d'y penser.

Les processus et la supervision ​

Le serveur est un ensemble de petits processus isolés : la connexion à la base, le serveur web… Un superviseur les surveille et redémarre celui qui plante. C'est la philosophie « let it crash » : on ne cherche pas à prévoir chaque panne, on sait repartir proprement. La liste est dans lib/timemanager/application.ex.

Phoenix, le framework web ​

NotionCe que c'estDans le projet
Endpointla porte d'entrée : tout ce que chaque requête traverselib/timemanager_web/endpoint.ex
Plugune fonction qui reçoit la requête (conn) et la renvoie modifiée, ou l'arrête (halt)Plugs.Authenticate vérifie le jeton
Routerl'aiguillage : quelle adresse va à quel contrôleurlib/timemanager_web/router.ex
Pipelineune suite de plugs appliquée à un groupe de routes:api (JSON seulement), :authenticated
Contrôleurreçoit la requête, appelle le métier, choisit la réponseClockController, UserController…
Vue JSONchoisit exactement les champs envoyésClockJSON, UserJSON…
Fallbacktransforme toute erreur en réponse JSONFallbackController

Pourquoi une vue JSON, et pas le schéma tel quel ?

On n'envoie jamais un enregistrement de la base directement. La vue choisit chaque champ : un champ interne, comme l'empreinte du mot de passe d'un compte, ne peut donc jamais fuiter par oubli.

Ecto, l'accès à la base ​

NotionCe que c'estExemple
Schémala forme d'une table en ElixirClocks.Clock pour la table clocks
Changesetune modification vérifiée : ce qu'on accepte, les règles, les erreursCorrectionRequest.create_changeset/2
Repoce qui parle vraiment à PostgreSQLRepo.insert/1, Repo.transaction/1
Migrationun fichier qui crée ou modifie une table, dans l'ordrepriv/repo/migrations/
Requêtedu SQL écrit en Elixirfrom u in User, where: u.id == ^id

Un changeset est la notion clé. Il garde la liste des changements et des erreurs. Si une règle échoue, rien n'est écrit, et l'erreur repart vers le client avec un code stable (voir les erreurs 422 dans Swagger).

Le contexte : le métier rangé par domaine ​

Un contexte est un module qui regroupe un domaine du métier et cache la base derrière des fonctions claires :

ContexteDomaine
Accountscomptes, mots de passe, liens envoyés par e-mail
Auth.Sessionssessions et rotation des jetons
Authorizationle seul endroit où les droits sont décidés
Clocksles pointages d'arrivée et de départ
Workingtimeles journées, les pauses, l'historique des modifications
Teamsles équipes et leurs membres
Correctionsles demandes de correction
Absencesles jours d'absence
WeekValidationsles semaines validées
Notificationsla cloche et ses e-mails
Reportsles totaux et les moyennes
PersonalDatala copie de « Mes données »

Un contrôleur n'écrit jamais de requête SQL : il demande au contexte, par exemple Clocks.clock(user_id, params).

Pour aller plus loin ​