\documentclass{slides} \addbibresource{slides.bib} \title{Systèmes à objets répartis} \subtitle{M1 Informatique, S8} \date{Janvier 2026} \titlegraphic{\flushright\includegraphics[height=1.3cm]{img/logos.png}\vspace{.5cm}} \author{ \texorpdfstring{ \begin{table}[] \begin{flushleft} \begin{tabular}{ll} & Vincent Lannurien~\inst{1} \\ & \url{vincent.lannurien@univ-brest.fr} \\ \end{tabular} \end{flushleft} \end{table} } { Vincent Lannurien } } \institute{ \texorpdfstring{ \begin{table}[] \centering \begin{flushleft} \begin{tabular}{L} \inst{1}~Lab-STICC, CNRS UMR 6285, Université de Bretagne Occidentale, Brest \end{tabular} \end{flushleft} \end{table} } { Lab-STICC, CNRS UMR 6285, Université de Bretagne Occidentale, Brest } } \begin{document} \maketitle \begin{frame}{Introduction} \begin{center} \url{http://info-demo-sor.univ-brest.fr:3000} \end{center} \end{frame} \begin{framefont}{\footnotesize} \begin{frame}[t]{Plan du cours} \begin{columns} \column{0.6\textwidth} \tableofcontents%[hideallsubsections] \column{0.4\textwidth} \begin{center} \includegraphics[width=\columnwidth]{img/screenshot.png} \end{center} \end{columns} \end{frame} \end{framefont} \section{Organisation de l'UE} \begin{frame}{Organisation de l'UE -- Contenu} \begin{itemize} \item Objectifs : \begin{itemize} \item Développer une application suivant l'\textbf{architecture trois tiers}, s'appuyant sur des communications via \textbf{HTTP} et \textbf{WebSockets} ; \item Comprendre les mécanismes de l'\textbf{authentification} (avec ou sans état) d'un client auprès d'un serveur ; \item S'initier au \textbf{déploiement} d'une application répartie à l'aide d'un \textit{reverse proxy}. \end{itemize} \item Prérequis : \begin{itemize} \item Côté client : HTML/CSS \end{itemize} \item Méthode : \begin{itemize} \item Un peu de cours... \item Beaucoup de pratique ! \end{itemize} \end{itemize} \end{frame} \begin{frame}{Organisation de l'UE -- Logistique} \begin{itemize} \item Volume horaire : \begin{itemize} \item CM : 3 séances de 2h (total : 6h) \item TP : 8 séances de 2h (total : 16h) \end{itemize} \item Contrôle des connaissances : \begin{itemize} \item TP noté (2h sur cette partie du cours) \item Examen final (2h en tout) \end{itemize} \end{itemize} \end{frame} \section{Introduction} \begin{frame}{Contenu du cours} \begin{itemize} \item État et gestion de l'état \begin{itemize} \item Contrats \begin{itemize} \item Interfaces \item \textit{Data Transfer Objects} \end{itemize} \item Persistance \begin{itemize} \item Base de données \item Transactions % \item Cache \end{itemize} \end{itemize} \item Protocoles \begin{itemize} \item HTTP \begin{itemize} \item \textit{Status codes} \item \textit{CORS} (\textit{Cross-Origin Resource Sharing}) \end{itemize} \item Authentification \begin{itemize} \item \textit{Tokens} \end{itemize} \end{itemize} \item Performances et profilage \begin{itemize} \item Instrumentation (serveur) \item Inspecteur (client) \item Injection de trafic \end{itemize} \item Déploiement \begin{itemize} \item SSL, certificats \item \textit{Reverse proxy} \end{itemize} \end{itemize} \end{frame} \begin{frame}{Technologies utilisées} \begin{itemize} \item Côté serveur : \begin{itemize} \item Runtime (Deno) \item Langage (TypeScript) \item Framework (Oak) \item Base de données (SQLite) % \item Cache (Redis) \end{itemize} \item Côté client : \begin{itemize} \item Bundler (Vite) \item Langage (TypeScript) \item Framework (React) \end{itemize} \item Communications : \begin{itemize} \item Requêtes HTTP \item WebSockets \end{itemize} \end{itemize} \end{frame} \section{Cas d'étude applicatif} \begin{frame}{Présentation de l'application} \begin{block}{Description} L'application est une \textbf{plateforme de sondages en ligne}. Elle permet à des utilisateurs de \textbf{créer des sondages} et d'ajouter des \textbf{options de réponse}. Les participants peuvent \textbf{voter} pour une ou plusieurs options selon des règles définies par le créateur du sondage. L'application gère également l'\textbf{authentification} des utilisateurs et assure la \textbf{persistance} des données. \end{block} \end{frame} \begin{frame}{Présentation de l'application} \begin{alertblock}{Acteurs} Les acteurs de l'application sont les suivants : \begin{itemize} \item Utilisateur authentifié : peut créer des sondages, voter, et consulter les résultats ; \item Utilisateur invité : peut voter (si autorisé) et consulter les résultats (si autorisé) ; \item Administrateur : peut gérer les sondages et les utilisateurs. \end{itemize} \end{alertblock} \end{frame} \begin{frame}{Présentation de l'application} \begin{alertblock}{Fonctionnalités} Les principales fonctionnalités de l'application peuvent être résumées ainsi : \begin{itemize} \item Création de sondages avec : titre, description, date de création, date d'expiration, statut (actif/inactif) ; \item Ajout d'options à un sondage : texte descriptif ; \item Vote pour une option de sondage ; \item Consultation des résultats (nombre de votes par option) ; \item Gestion des utilisateurs (inscription, authentification). \end{itemize} \end{alertblock} \end{frame} \section{Architecture : modèle client/serveur} \begin{frame}{Vue d'ensemble} \centering \includegraphics[width=.9\textwidth]{img/architecture.png} \end{frame} \begin{frame}{Monolithes ou microservices} \centering \includegraphics[width=.8\textwidth]{img/scaling-monolith.png} \addtocounter{footnote}{1} \footnotetext{\fullcite{fowlerMicroservices}} \end{frame} \begin{frame}{Monolithes ou microservices} \centering \includegraphics[width=.8\textwidth]{img/scaling-uservices.png} \footnotetext{\fullcite{fowlerMicroservices}} \end{frame} \section{JavaScript, TypeScript et \textit{runtime}} \begin{frame}{JavaScript} \centering \includegraphics[width=.5\textwidth]{img/javascript-alert.png} \end{frame} \begin{frame}{JavaScript} \begin{columns} \column{.8\textwidth} \begin{itemize} \item 1995 : Brendan Eich développe en 10 jours un langage simple, interprété par le navigateur, pour dynamiser les pages web \item 1997 : première standardisation par ECMA (\textit{European Computer Manufacturers Association}) sous le nom ECMAScript \item 2005 : AJAX popularise les applications web dynamiques \item 2008 : Google publie la machine virtuelle V8 pour l'exécution du code JavaScript dans Chrome \item 2009 : Node.js étend l'utilisation de JavaScript au côté serveur \item 2010 -- 2014 : émergence des frameworks et d'un outillage moderne pour JavaScript (jQuery, AngularJS, etc.) \item 2015 : ECMAScript Edition 6 (ES6/ES2015) transforme le langage (modules, classes, \textit{arrow functions}, \textit{Promises}) \end{itemize} \column{.2\textwidth} \includegraphics[width=\columnwidth]{img/js.png} \includegraphics[width=\columnwidth]{img/netscape.png} \includegraphics[width=\columnwidth]{img/nodejs.png} \end{columns} \end{frame} \begin{frame} \begin{columns} \column{.6\textwidth} \centering \includegraphics[width=.9\columnwidth]{img/jetbrains-ecosystem-2025-evolution.png} \column{.4\textwidth} \begin{itemize} \item \textit{"Among the most dramatic rises in real-world usage over the last five years is TypeScript."} \end{itemize} \end{columns} \end{frame} \begin{frame} \centering \includegraphics[width=.7\columnwidth]{img/jetbrains-ecosystem-2025-platforms.png} \addtocounter{footnote}{1} \footnotetext{\url{https://devecosystem-2025.jetbrains.com/tools-and-trends}} \end{frame} \begin{frame}{TypeScript} \begin{itemize} \item Conçu en 2012 par Anders Hejlsberg \item Open source, maintenu par Microsoft \item Ni interprété, ni compilé : langage \textbf{transpilé} vers JavaScript, \textit{superset} de JS \item Exécutable dans n'importe quel \textit{runtime} JS (navigateur, Node.js, etc.) \item Multi-paradigme : \textbf{fonctionnel}, générique, impératif, orienté objet \end{itemize} \end{frame} \begin{frame}{TypeScript} \begin{columns} \column{.8\textwidth} \begin{itemize} \item \textbf{Typage statique} : détecter les erreurs et incohérences à la compilation (donc avant l'exécution) \item \textbf{Interopérabilité} : typage \textit{progressif} (tout fichier JavaScript valide est un fichier TypeScript valide) \end{itemize} \column{.2\textwidth} \includegraphics[width=\columnwidth]{img/ts.png} \end{columns} Adoption par l'industrie : \begin{itemize} \item Slack~\footnote[frame]{\url{https://slack.engineering/typescript-at-slack/}} : migration du frontend React vers TS pour sécuriser les appels d'API et réduire les erreurs au runtime \item Airbnb~\footnote[frame]{\url{https://www.youtube.com/watch?v=P-J9Eg7hJwE}} : typage progressif du code JS pour réduire les coûts de maintenance \item Microsoft~\footnote[frame]{\url{https://www.git-tower.com/blog/developing-for-the-desktop-vscode}} : migration vers TS pour faciliter l'évolution de VS Code ($>$ 1M SLoC) \item Shopify~\footnote[frame]{\url{https://shopify.engineering/migrating-large-typescript-codebases-project-references}} : migration frontend et backend pour fiabiliser les fonctions critiques \end{itemize} \end{frame} \begin{frame}{TypeScript} \begin{itemize} \item Ressources : \begin{itemize} \item \textit{The TypeScript Handbook}~\footnote{\fullcite{HandbookTypeScriptHandbook}} \item \textit{The Concise TypeScript Book}~\footnote{\fullcite{poggialiConciseTypeScriptBook2026}} \item TypeScript TV~\footnote[frame]{\url{https://typescript.tv/}} (liste des codes d'erreur) \item TS Playground~\footnote[frame]{\url{https://www.typescriptlang.org/play/}} (environnement d'exécution en ligne) \end{itemize} \end{itemize} \end{frame} \subsection{Typage statique et dynamique} \begin{frame}{TypeScript : typage du code JavaScript} Types de base disponibles dans TypeScript~\footnote{\url{https://www.typescriptlang.org/docs/handbook/2/everyday-types.html}} : \begin{itemize} \item \texttt{boolean} \item \texttt{number} \item \texttt{string} \item Tableaux (dynamiques) -- exemple : \texttt{string[]} ou \texttt{Array} \item Tuples (taille fixe) -- exemple : \texttt{[string, number]} \item \texttt{enum} \item \texttt{unknown} (type à préciser) \item \texttt{any} (à éviter : toute valeur autorisée) \item \texttt{void} (absence de valeur) \item \texttt{undefined} (valeur non définie) \item \texttt{null} (valeur vide) \item \texttt{never} (valeur impossible) \end{itemize} \end{frame} \begin{frame}[fragile]{Exercice 1 : les pièges du typage dynamique} \begin{block}{Fonctions et paramètres} \begin{itemize} \item Écrire une fonction \texttt{addition(a, b)} qui additionne deux nombres. \item Tester la fonction avec les entrées suivantes : \end{itemize} \end{block} \begin{minted}{js} addition(2, 3); // attendu : 5 addition("2", 3); // attendu : ? addition(true, 3); // attendu : ? \end{minted} \begin{alertblock}{Questions} \begin{enumerate} \item Que se passe-t-il pour les deux derniers cas ? \item Quels problèmes cela pose-t-il poser dans une application réelle ? \item Proposer une version TypeScript pour éviter ces erreurs \textit{à la compilation}. \end{enumerate} \end{alertblock} \end{frame} \begin{frame}[fragile]{Exercice 1 : les pièges du typage dynamique} \begin{minted}{ts} function addition2(a: number, b: number): number { return a + b; } console.log(addition2(2, 3)); // L'argument de type 'string' n'est pas attribuable au paramètre // de type 'number'.ts(2345) console.log(addition2("2", 3)); // L'argument de type 'boolean' n'est pas attribuable au paramètre // de type 'number'.ts(2345) console.log(addition2(true, 3)); \end{minted} \end{frame} \begin{frame}[fragile]{JavaScript : POJO (\textit{Plain Old Java Object})} \begin{minted}{js} const o = { "foo": "bar", "value": 42, other: true, } console.log(o.foo); console.log(o["value"]); console.log(o.other); \end{minted} \end{frame} \begin{frame}[fragile]{POJO et listes : \textit{Spread operator}} \begin{minted}{ts} const o1 = { "abc": 123 }; // Ajout d'une nouvelle valeur dans `o1` o1["def"] = 456; // Nouvel objet avec copie des valeurs de `o1` const o2 = { ...o1, "ghi": 789 }; \end{minted} \vspace{.5cm} \begin{minted}{ts} const l1 = [123]; // Ajout d'une nouvelle valeur à la fin de `l1` l1.append(456); // Nouvelle liste avec copies des valeurs de `l1` const l2 = [...l1, 789]; \end{minted} \end{frame} \begin{frame}[fragile]{TypeScript : typer les objets avec \texttt{Record}} \begin{minted}{js} const o: Record = { "foo": "bar", "value": 42, other: true, } console.log(o.foo); console.log(o["value"]); console.log(o.other); console.log(o.blabla); // undefined \end{minted} \end{frame} \begin{frame}[fragile]{Typage, interfaces et classes} \begin{columns} \column{.5\textwidth} \begin{minted}{ts} // Définition interface Bidule { truc: string; machin: number; toto?: boolean; } // Instanciation const b: Bidule = { truc: "bidule", machin: 42, }; \end{minted} \column{.5\textwidth} \begin{minted}{ts} // Définition class Bidule { constructor( public truc: string, public machin: number, public toto?: boolean, ) {} } // Instanciation const b: Bidule = new Bidule( "bidule", 42 ); \end{minted} \end{columns} \end{frame} \begin{frame}[fragile]{Exercice 2 : interfaces et contrats} \begin{block}{Typage des objets} \begin{itemize} \item Écrire une fonction \texttt{afficherUtilisateur(u)} qui prend un objet représentant un utilisateur et affiche son nom et son âge. \item Tester la fonction avec les entrées suivantes : \end{itemize} \end{block} \begin{minted}{js} afficherUtilisateur({ nom: "Alice", age: 25 }); afficherUtilisateur({ nom: "Bob" }); \end{minted} \begin{alertblock}{Questions} \begin{enumerate} \item Que se passe-t-il pour le second cas ? \item Comment TypeScript peut-il garantir la présence des propriétés attendues ? \item Proposer une solution avec TypeScript. \end{enumerate} \end{alertblock} \end{frame} \begin{frame}[fragile]{Exercice 2 : interfaces et contrats} \begin{minted}{ts} interface Utilisateur { nom: string; age: number; } function afficherUtilisateur(u: Utilisateur): void { console.log(`${u.nom} a ${u.age} ans`); } afficherUtilisateur({ nom: "Alice", age: 25 }); // L'argument de type '{ nom: string; }' n'est pas attribuable // au paramètre de type 'Utilisateur'. // La propriété 'age' est absente du type '{ nom: string; }' // mais obligatoire dans le type 'Utilisateur'.ts(2345) afficherUtilisateur({ nom: "Bob" }); \end{minted} \end{frame} \begin{frame}{Typage, interfaces et classes} \begin{itemize} \item Types \textbf{effacés} à la compilation \begin{itemize} \item Pas d'impact sur les performances (compromis : temps de compilation vs temps d'exécution) \item Une erreur de typage n'empêche pas la compilation vers JavaScript \end{itemize} \item Corollaire : il n'est pas possible de vérifier les types TypeScript \textbf{lors de l'exécution} du programme... \item ... sauf pour les types primitifs de JavaScript ! \end{itemize} \end{frame} \begin{frame}[fragile]{Typage, interfaces et classes} \begin{minted}{ts} // type union : soit number, soit string const fn = (x: number | string) => { // typeof type guard if (typeof x === 'number') { return x + 1; } return -1; }; \end{minted} \begin{minted}{ts} // type union : soit string, soit null const toUpperCase = (name: string | null) => { // truthiness narrowing if (name) { return name.toUpperCase(); } else { return null; } }; \end{minted} \end{frame} \begin{frame}[fragile]{Exercice 3 : types au \textit{runtime}} \begin{minted}[fontsize=\footnotesize]{ts} interface Animal { name: string; } interface Dog extends Animal { bark: () => void; } interface Cat extends Animal { meow: () => void; } const makeNoise = (animal: Animal) => { if (animal instanceof Dog) { // 'Dog' fait référence à un type mais s'utilise en tant que valeur ici.ts(2693) animal.bark(); } else { animal.meow(); } }; \end{minted} \begin{alertblock}{Questions} \begin{enumerate} \item Que signifie l'erreur levée par le compilateur ? \item Comment peut-on vérifier la nature de l'objet \texttt{animal} au \textit{runtime} ? \end{enumerate} \end{alertblock} \end{frame} \begin{frame}[fragile]{Exercice 3 : types au \textit{runtime}} \begin{minted}{ts} interface Dog { kind: "dog"; // Tagged union bark: () => void; } interface Cat { kind: "cat"; // Tagged union meow: () => void; } type Animal = Dog | Cat; // Union type const makeNoise = (animal: Animal) => { if (animal.kind === "dog") animal.bark(); else animal.meow(); }; \end{minted} \end{frame} \begin{frame}[fragile]{Exercice 3 : types au \textit{runtime}} \begin{minted}{ts} class Dog { constructor(public name: string, public bark: () => void) {} } class Cat { constructor(public name: string, public meow: () => void) {} } type Animal = Dog | Cat; // Union type const makeNoise = (animal: Animal) => { if (animal instanceof Dog) animal.bark(); else animal.meow(); }; \end{minted} \end{frame} \begin{frame}[fragile]{Exercice 3 : types au \textit{runtime}} \begin{minted}[fontsize=\footnotesize]{ts} interface Dog { kind: "dog"; // Tagged union bark: () => void; } interface Cat { kind: "cat"; // Tagged union meow: () => void; } type Animal = Dog | Cat; const makeNoise = (animal: Animal) => { if (animal.kind === "dog") animal.bark(); else animal.meow(); }; const dog: Dog = { kind: "dog", bark: () => console.log("bark") }; makeNoise(dog); \end{minted} \end{frame} \begin{frame}[fragile]{Type Guards : \textit{compile time} et \textit{run time}} \begin{itemize} \item \textit{Type narrowing} : \textit{compile time} \item \textit{Type assertion} : \textit{run time} \end{itemize} \vspace{.5cm} \begin{minted}{ts} interface Something { id: string }; export function isSomething(obj: unknown): obj is Something { return !!obj && typeof obj === "object" && "id" in obj && typeof obj.id === "string"; } const bla = { id: "blabla" }; \end{minted} \end{frame} \subsection{Généricité} \begin{frame}[fragile]{Généricité} \begin{itemize} \item \texttt{SomeContainer} \begin{itemize} \item \texttt{T} est un type paramètre contraint à hériter ou implémenter \texttt{SomeData} \item Permet de réutiliser le même conteneur pour différents types de données \end{itemize} \item Objectifs : \begin{itemize} \item Définir des structures et fonctions abstraites sur un type variable \item Maintenir les contraintes de type \end{itemize} \end{itemize} \begin{columns} \column{.6\textwidth} \begin{minted}[fontsize=\footnotesize]{ts} interface SomeData { id: string; } interface SpecializedData extends SomeData { name: string; value: number; } interface SomeContainer { data: T; } \end{minted} \column{.4\textwidth} \begin{minted}[fontsize=\footnotesize]{ts} const c: SomeContainer< SpecializedData > = { data: { id: "foo", name: "Foo Bar", value: 42, }, }; \end{minted} \end{columns} \end{frame} \subsection{Gestion des erreurs} \begin{frame}[fragile]{Gestion des erreurs : exceptions} \begin{minted}{ts} const randInt = Math.floor(Math.random() * 3) + 1; // [1, 3] const error = randInt === 1 ? new TypeError() : randInt === 2 ? new RangeError() : new EvalError(); try { throw error; } catch { // handle error } \end{minted} \end{frame} \begin{frame}[fragile]{Gestion des erreurs : exceptions} \begin{minted}{ts} const randInt = Math.floor(Math.random() * 3) + 1; // [1, 3] const error = randInt === 1 ? new TypeError() : randInt === 2 ? new RangeError() : new EvalError(); try { throw error; } catch (e) { if (e instanceof TypeError) { // handle TypeError } else if (e instanceof RangeError) { // handle RangeError } else if (e instanceof EvalError) { // handle EvalError } } \end{minted} \end{frame} \begin{frame}[fragile]{Gestion des erreurs : exceptions} \begin{minted}{ts} try { const file = await Deno.open("example.txt", { read: true }); for await (const chunk of Deno.iter(file)) { console.log(new TextDecoder().decode(chunk)); } } catch (e) { console.error("Error reading file:", e); } finally { // always close the file handle if (file) { file.close(); } } \end{minted} \end{frame} \subsection{Asynchronisme et parallélisme} \begin{frame}{Programmation asynchrone} Comment manipuler des données... \begin{itemize} \item que l'on n'a pas encore ? \item que l'on n'aura peut-être jamais ? \item qui l'on obtient dans un temps variable ? \end{itemize} Par exemple... \begin{itemize} \item le résultat d'une requête à une API ? \item le contenu d'un fichier volumineux sur le disque ? \item le résultat d'une requête à une base de données ? \end{itemize} \end{frame} \begin{frame}{Programmation asynchrone} \centering \includegraphics[width=.9\textwidth]{img/async.png} \end{frame} \begin{frame}{Programmation asynchrone} \begin{itemize} \item API : \textbf{Promises} \begin{itemize} \item Traiter des opérations \textbf{asynchrones} \item Une Promise \textit{encapsule un résultat futur} \item Elle est associée à des \textit{callbacks} qui seront exécutés à l'obtention de ce résultat \item Elle peut être \textit{résolue} (réussite) ou \textit{rejetée} (échec) \end{itemize} \end{itemize} \centering \includegraphics[width=.8\textwidth]{img/promises.png} \addtocounter{footnote}{1} \footnotetext{\url{https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise}} \end{frame} \begin{frame}[fragile]{Programmation asynchrone} \begin{minted}[fontsize=\footnotesize]{ts} function repeatMaybe(repeat: string): Promise { return new Promise((resolve, reject) => { const maybe: number = randomIntegerBetween(0, 1); if (!maybe) { reject("Nope"); return; } setTimeout(() => { resolve(repeat); }, 1000); }); } const blabla = repeatMaybe("bla").then((result: string) => { console.log(result); }).catch((error: string) => { console.log(error) }); \end{minted} \end{frame} \begin{frame}[fragile]{Programmation asynchrone} \begin{columns} \column{.5\textwidth} \begin{itemize} \item API : \texttt{async}/\texttt{await} \begin{itemize} \item Sucre syntaxique pour "aplatir" du code asynchrone \item \texttt{async} définit une fonction asynchrone \item \texttt{await} met l'exécution du programme en pause dans l'attente d'un résultat \end{itemize} \end{itemize} \column{.5\textwidth} \centering \includegraphics[width=.7\columnwidth]{img/async-joke.jpg} \end{columns} \vspace{1cm} \begin{columns} \column{.5\textwidth} \begin{minted}[fontsize=\footnotesize]{ts} asyncFunction().then((res) => { console.log(res); }).catch((err) => { console.log(err); }); \end{minted} \column{.5\textwidth} \begin{minted}[fontsize=\footnotesize]{ts} try { const res = await asyncFunction(); console.log(res); // "bla" } catch (err) { console.log(err); // "Nope" } \end{minted} \end{columns} \end{frame} \begin{frame}[fragile]{Programmation asynchrone} \begin{minted}[fontsize=\footnotesize]{ts} function repeatMaybe(repeat: string): Promise { return new Promise((resolve, reject) => { const maybe: number = randomIntegerBetween(0, 1); if (!maybe) { reject("Nope"); return; } setTimeout(() => { resolve(repeat); }, 1000); }); } try { const blabla = await repeatMaybe("bla"); console.log(blabla); // "bla" } catch (error) { console.log(error); // "Nope" } \end{minted} \end{frame} \begin{frame}{Programmation parallèle} \begin{itemize} \item \texttt{Promise.all} \begin{itemize} \item exécute toutes les Promises en \textbf{parallèle} \item résout quand toutes les Promises ont \textbf{réussi} \item rejette immédiatement si \textbf{une seule échoue} \item retourne un tableau des résultats dans l'ordre initial des Promises \end{itemize} \item \texttt{Promise.allSettled} \item \begin{itemize} \item exécute toutes les Promises en \textbf{parallèle} \item résout quand toutes les Promises sont terminées, \textbf{succès ou échec} \item ne rejette \textbf{jamais} \item retourne un tableau d'objets de type : \begin{itemize} \item \texttt{{status: 'fulfilled' | 'rejected', value | reason}} \end{itemize} \end{itemize} \end{itemize} \end{frame} \begin{frame}[fragile]{Exercice 4 : programmation asynchrone} \begin{block}{Manipuler l'API Promise} \begin{itemize} \item Écrire une fonction \texttt{readTextFileIfExists(path)} qui lit un fichier s'il existe et retourne son contenu si le fichier n'est pas vide : \end{itemize} \end{block} \begin{minted}{js} readTextFileIfExists("data.txt") .then(text => console.log("File contents:", text)) .catch(err => console.error("I/O error:", err.message)); \end{minted} \begin{alertblock}{Questions} \begin{enumerate} \item Quelle est la signature de cette fonction en TypeScript ? \item Comment utiliser cette fonction avec \texttt{await} ? \item Comment utiliser cette fonction sur une liste de fichiers ? \item Comment paralléliser la lecture des fichiers de la liste ? \end{enumerate} \end{alertblock} \end{frame} \begin{frame}[fragile]{Exercice 4 : programmation asynchrone} \begin{minted}[fontsize=\footnotesize]{js} function readTextFileIfExists(path) { return new Promise((resolve, reject) => { if (!path) { reject(new Error("No file path provided")); return; } readFile(path, "utf8") .then(data => { if (data.length === 0) { reject(new Error("File is empty")); // explicit failure } else { resolve(data); // successful read } }) .catch(err => reject(err)); // I/O error }); } \end{minted} \end{frame} \begin{frame}[fragile]{Exercice 4 : programmation asynchrone} \begin{minted}{js} try { const text = await readTextFileIfExists("data.txt"); console.log("File contents:", text); } catch (err) { console.error("I/O error:", err.message); } \end{minted} \end{frame} \begin{frame}[fragile]{Exercice 4 : programmation asynchrone} \begin{minted}{js} const files = ["file1.txt", "file2.txt", "file3.txt"]; for (const file of files) { try { const text = await readTextFileIfExists(file); console.log("File contents:", text); } catch (err) { console.error("I/O error:", err.message); continue; } } \end{minted} \vspace{.5cm} \begin{block}{\emoji{thinking}} Temps d'exécution total = ? \end{block} \end{frame} \begin{frame}[fragile]{Exercice 4 : programmation asynchrone} \begin{minted}{js} const files = ["does_not_exist.txt", "file2.txt", "file3.txt"]; const contents = await Promise.all( files.map(readTextFileIfExists) ); try { contents.forEach((text, i) => { console.log("File contents:", text); }); } catch (err) { console.error("I/O error:", err.message); } \end{minted} \vspace{.5cm} \begin{block}{\emoji{thinking}} Temps d'exécution total = ? \end{block} \end{frame} \begin{frame}[fragile]{Exercice 4 : programmation asynchrone} \begin{minted}{js} const files = ["does_not_exist.txt", "file2.txt", "file3.txt"]; const contents = await Promise.allSettled( files.map(readTextFileIfExists) ); contents.forEach((result, i) => { if (result.status === "fulfilled") { console.log("File contents:", result.value); } else if (result.status === "rejected") { console.error("I/O error:", result.reason); } }); \end{minted} \vspace{.5cm} \begin{block}{\emoji{thinking}} Temps d'exécution total = ? \end{block} \end{frame} \subsection{Environnement d'exécution} \begin{frame}{Exécution : machine virtuelle} \begin{itemize} \item JavaScript : langage interprété dans une VM \item Langage permet d'exprimer des instructions \textbf{asynchrones} \item Runtime \textbf{synchrone} : boucle d'événements (pas de coroutines, pas de threads~\footnote{Sauf \textit{workers}, voir~\url{https://developer.mozilla.org/en-US/docs/Glossary/Thread}}) \end{itemize} \centering \includegraphics[width=\textwidth]{img/js-event-loop.png} \end{frame} \begin{frame}{Runtime : Deno} \begin{columns} \column{.8\textwidth} \begin{itemize} \item Runtime pour exécuter du code JavaScript en-dehors du navigateur \item Basé sur le moteur V8 (comme Node.js) \item Créé en 2018 par Ryan Dahl, développeur de Node.js~\footnote[frame]{\url{https://www.youtube.com/watch?v=M3BM9TB-8yA}} \item Points forts : \begin{itemize} \item \textit{Sandbox} : permissions explicites (lecture/écriture, accès au réseau, etc.) \item Exécute du code TypeScript sans outillage supplémentaire \item Inclut un formateur, un \textit{linter}, une plateforme de test, un profileur \end{itemize} \end{itemize} \column{.2\textwidth} \includegraphics[width=\columnwidth]{img/deno.png} \end{columns} \end{frame} \begin{frame}{Sécurité, permissions} \begin{itemize} \item \textit{Supply-chain attack} : introduction de vulnérabilités dans un logiciel \textit{via} compromission d'une ou plusieurs de ses dépendances \item Exemple : \textit{Shai Hulud}, novembre 2025~\footnote[frame]{\url{https://www.cert.ssi.gouv.fr/actualite/CERTFR-2025-ACT-051/}}, 700 paquets compromis sur NPM \begin{itemize} \item récupération de différents secrets présents sur la machine compromise [...] ; \item exfiltration des secrets ; \item réplication par l'infection de paquets NPM ; \item suppression de données utilisateurs ; \item mise en place de persistance. \end{itemize} \end{itemize} \end{frame} \begin{frame}{Gestion des dépendances} \begin{columns} \column{.8\textwidth} Outils et registres : \begin{itemize} \item \texttt{npmjs.org} : registre historique des paquets JavaScript \begin{itemize} \item Gestionnaires de paquets : \textbf{npm / yarn} \end{itemize} \item \texttt{jsr.org} : "nouveau" registre, orienté TypeScript \begin{itemize} \item Gestionnaire de paquets : \textbf{jsr}, intégré dans Deno \end{itemize} \end{itemize} \vspace{.5cm} Modules et imports : \begin{itemize} \item \textbf{CommonJS (CJS)} : compatibilité historique \begin{itemize} \item Import : \texttt{const fs = require('fs');} \item Export : \texttt{module.exports = \{ readFile \};} \item Chargement synchrone \end{itemize} \item \textbf{ECMAScript Modules (ESM)} : standard moderne \begin{itemize} \item Import : \texttt{import \{ readFile \} from 'fs/promises';} \item Export : \texttt{export const myFunc = () => \{...\};} \item Chargement statique ou dynamique (asynchrone) \end{itemize} \end{itemize} \column{.2\textwidth} \includegraphics[width=\columnwidth]{img/npm.png} \includegraphics[width=\columnwidth]{img/jsr.png} \end{columns} \end{frame} \section{Serveur web} \begin{frame}{\textit{It's a series of tubes...}} \centering \includegraphics[width=.6\textwidth]{img/series-of-tubes.png} \end{frame} \begin{frame}{Résumé de la méthode en TP} \begin{enumerate} \item La base de données retourne des \textbf{enregistrements} : ce sont des données arbitraires \begin{itemize} \item Élément unique : \texttt{Record | undefined} \begin{itemize} \item Peut être \texttt{undefined} si introuvable ! \end{itemize} \item Liste : \texttt{Record[]} \begin{itemize} \item Peut être vide ! \end{itemize} \end{itemize} \item On doit \textit{vérifier le typage des données} pour que l'objet se conforme à son \textit{interface base de données} (\texttt{PollRow}, ...) \item On peut alors \textit{convertir l'enregistrement} en objet de notre \textit{API} (\texttt{Poll}, ...) \item L'objet peut être retourné au client de manière \textit{sûre} \begin{itemize} \item \textit{Compile time} : typage statique des données dans l'application client \item \textit{Run time} : pas d'erreur à l'exécution pour l'utilisateur \end{itemize} \end{enumerate} \end{frame} \begin{frame} \centering \includegraphics[width=\textwidth]{img/type-guard-1.png} \end{frame} \begin{frame}[noframenumbering] \centering \includegraphics[width=\textwidth]{img/type-guard-2.png} \end{frame} \begin{frame}[noframenumbering] \centering \includegraphics[width=\textwidth]{img/type-guard-3.png} \end{frame} \begin{frame}[noframenumbering] \centering \includegraphics[width=\textwidth]{img/type-guard-4.png} \end{frame} \begin{frame}[noframenumbering] \centering \includegraphics[width=\textwidth]{img/type-guard-5.png} \end{frame} \begin{frame}[fragile]{Résumé de la méthode en TP} \begin{columns} \column{.5\textwidth} \begin{minted}{ts} export interface PollRow { id: string; title: string; description: string | null; user_id: string | null; created_at: string; expires_at: string | null; is_active: number; [key: string]: SQLOutputValue; } \end{minted} \column{.5\textwidth} \begin{minted}{ts} export interface Poll { id: string; title: string; description?: string; options: PollOption[]; userId?: string; createdAt: string; expiresAt?: string; isActive: boolean; } \end{minted} \end{columns} \end{frame} \begin{frame}[fragile]{Résumé de la méthode en TP} \begin{minted}[fontsize=\small]{ts} export function isPollRow( obj: Record ): obj is PollRow { return !!obj && typeof obj === "object" && "id" in obj && typeof obj.id === "string" && "title" in obj && typeof obj.title === "string" && "description" in obj && (typeof obj.description === "string" || obj.description === null) && "user_id" in obj && (typeof obj.user_id === "string" || obj.user_id === null) && "created_at" in obj && typeof obj.created_at === "string" && "expires_at" in obj && (typeof obj.expires_at === "string" || obj.expires_at === null) && "is_active" in obj && typeof obj.is_active === "number"; } \end{minted} \end{frame} \begin{frame}[fragile]{Résumé de la méthode en TP} \begin{minted}{ts} export function pollRowToApi( row: PollRow, optionRows: PollOptionRow[], ): Poll { return { id: row.id, title: row.title, description: row.description ?? undefined, createdAt: row.created_at, expiresAt: row.expires_at ?? undefined, isActive: row.is_active === 1, options: optionRows.map(pollOptionRowToApi), }; } \end{minted} \end{frame} \begin{frame}[fragile]{Résumé de la méthode en TP} \begin{minted}{ts} export function pollApiToRow( poll: Poll ): [PollRow, PollOptionRow[]] { return [ { id: poll.id, title: poll.title, description: poll.description ?? null, user_id: poll.userId ?? null, created_at: poll.createdAt, expires_at: poll.expiresAt ?? null, is_active: Number(poll.isActive), }, poll.options.map((pollOption) => pollOptionApiToRow( poll.id, pollOption )), ]; } \end{minted} \end{frame} \subsection{Protocole HTTP} \begin{frame}{HTTP} \begin{columns} \column{.6\textwidth} \begin{itemize} \item \textit{Hypertext Transfer Protocol} : à l'origine, sert à partager des pages web reliées entre elles par des liens \item Inventé au CERN~\footnote[frame]{Initialement \textit{Conseil européen pour la recherche nucléaire}, plus grand centre de recherche au monde en physique des particules} par un chercheur britannique, Tim Berners-Lee, entre 1989 et 1991 \item 1997 : standardisation de HTTP/1.1 : \begin{itemize} \item Apparition de l'en-tête \texttt{Host}, permettant d'héberger plusieurs domaines sur la même adresse IP, donc la colocation de serveurs web \end{itemize} \end{itemize} \begin{center} \includegraphics[width=.4\columnwidth]{img/tbl.jpg} \end{center} \column{.4\textwidth} \includegraphics[width=\columnwidth]{img/osi-model.png} \end{columns} \end{frame} \begin{frame}{HTTP : requêtes et réponses} \centering \includegraphics[width=\textwidth]{img/http-message.png} \end{frame} \begin{frame}{HTTP : méthodes} Méthodes principales pour les requêtes HTTP~\footnote[frame]{\url{https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Methods}}, aussi appelées \textit{verbes} : \begin{itemize} \item \texttt{GET} : demande d'une ressource \item \texttt{POST} : envoi de données du \textit{corps} de la requête vers une ressource \item \texttt{PUT} : remplacement d'une ressource par le \textit{corps} de la requête \item \texttt{DELETE} : suppression d'une ressource \end{itemize} \end{frame} \begin{frame}{HTTP : codes de réponse} \begin{itemize} \item \texttt{100} à \texttt{199} : réponses informatives \begin{itemize} \item \texttt{101 Switching Protocols} \end{itemize} \item \texttt{200} à \texttt{299} : réponses de succès \begin{itemize} \item \texttt{200 OK} \end{itemize} \item \texttt{300} à \texttt{399} : réponses de redirection \begin{itemize} \item \texttt{301 Moved Permanently} \end{itemize} \item \texttt{400} à \texttt{499} : erreurs du client \begin{itemize} \item \texttt{401 Unauthorized} \end{itemize} \item \texttt{500} à \texttt{599} : erreurs du serveur \begin{itemize} \item \texttt{500 Internal Server Error} \end{itemize} \end{itemize} \end{frame} \begin{frame}{HTTP : architecture REST} \begin{columns} \column{.7\textwidth} \begin{itemize} \item \textit{REpresentational State Transfer}~\cite{fielding2000architectural} : défini par Roy Fielding en 2000, conçu pendant son doctorat (1996 - 1999) \item Modèle d'architecture logicielle pour applications réseau \item Manipuler des ressources distantes via des représentations textuelles \item Opérations uniformes, prédéfinies, et \textbf{sans état} \end{itemize} \column{.3\textwidth} \includegraphics[width=\columnwidth]{img/roy_fielding_OSCON_2008.jpg} \end{columns} \end{frame} \begin{frame} \begin{center} \includegraphics[width=.8\textwidth]{img/rest-wikipedia.png} \end{center} \end{frame} \begin{frame}[fragile]{Exercice 5 : un serveur HTTP minimal avec TypeScript} \begin{block}{Lire et écrire sur un socket TCP} Ci-dessous, la boucle principale pour ouvrir et écouter sur un socket TCP avec Deno. Dès la connexion d'un \textbf{client}, on passe le socket à la fonction \texttt{handleConn}. \end{block} \begin{minted}{ts} // TCP socket const listener = Deno.listen({ port: 8080 }); // Strings <-> Bytes const decoder = new TextDecoder(); const encoder = new TextEncoder(); // Listen loop for await (const conn of listener) { handleConn(conn); } \end{minted} \end{frame} \begin{frame}[fragile]{Exercice 5 : un serveur HTTP minimal avec TypeScript} \begin{block}{Lire et écrire sur un socket TCP} Ci-dessous, la fonction \texttt{handleConn} par laquelle chaque \textbf{requête} en provenance d'un \textbf{client} est traitée. Le serveur reçoit un \textbf{tableau d'octets} ; on le convertit en une chaîne de caractères dans \texttt{rawRequest}. \end{block} \begin{minted}{ts} async function handleConn(conn: Deno.Conn) { const buf = new Uint8Array(1024); const bytes = await conn.read(buf); if (bytes === null) { conn.close(); return; } const rawRequest = decoder.decode(buf.subarray(0, bytes)); } \end{minted} \end{frame} \begin{frame}[fragile]{Exercice 5 : un serveur HTTP minimal avec TypeScript} \begin{minted}{sh} curl -X POST http://localhost:8080 \ -H "Content-Type: text/plain" \ -d "hello world" \end{minted} \vspace{1cm} \begin{columns} \column{.4\textwidth} \begin{minted}{text} POST / HTTP/1.1 Host: localhost:8080 User-Agent: curl/8.14.1 Accept: */* Content-Type: text/plain Content-Length: 11 hello world \end{minted} \column{.6\textwidth} \begin{alertblock}{Questions} \begin{enumerate} \item Quels sont les champs à inclure dans la réponse que l'on va retourner au client ? \item Compléter la fonction \texttt{handleConn} pour signifier une réponse OK comportant du texte au format JSON. \item Comment va-t-on gérer l'horodatage ? \end{enumerate} \end{alertblock} \end{columns} \end{frame} \subsection{Mise en œuvre} \begin{frame}{Framework pour serveur HTTP : Oak} \begin{columns} \column{.8\textwidth} \begin{itemize} \item Inspiré par Express.js, initialement développé pour Deno \item Framework \textit{middleware} : un logiciel qui s'intercale entre la requête d'un client et la réponse d'un serveur \item \textbf{Contexte} $=$ \textbf{Requête} $+$ \textbf{Réponse} \item Mécanisme principal : le \textbf{routage} \begin{itemize} \item Pour un point d'entrée donné, appliquer telle fonction, retourner telle valeur \item Requête : \texttt{GET http://blabla.com/item/123} \item Méthode : \texttt{GET} \item Route : \texttt{/item/:itemId} \item Fonction : \texttt{getItem(id)} \item Réponse : \texttt{\{ id: 123, value: "..." \}} \end{itemize} \end{itemize} \column{.2\textwidth} \includegraphics[width=\columnwidth]{img/oak.png} \end{columns} \end{frame} \begin{frame}[fragile]{Framework pour serveur HTTP : Oak} \begin{minted}[fontsize=\footnotesize]{ts} // Configuration const BASE_URL = "http://localhost:8000"; // Initialisation const app = new Application(); const router = new Router(); // Middlewares app.use(router.routes()); // Encore faut-il définir des routes ! ... app.use(router.allowedMethods()); // Événements app.addEventListener( "listen", () => console.log(`Server listening on ${BASE_URL}`), ); app.addEventListener( "error", (e) => console.log(`Uncaught error: ${e.message}`), ); // Boucle principale if (import.meta.main) { await app.listen({ hostname: "localhost", port: 8000 }); } // Export export { app }; \end{minted} \end{frame} \begin{frame}[fragile]{Routage avec Oak : paramètres de la requête} \begin{itemize} \item L'utilisateur envoie une requête \texttt{GET} \item Adresse : \texttt{http://localhost:8000/polls/abcd} \end{itemize} \vspace{.5cm} \begin{minted}{ts} router.get("/:pollId", (ctx) => { const pollId = ctx.params.pollId; // abcd }); \end{minted} \end{frame} \begin{frame}[fragile]{Routage avec Oak : paramètres de la requête} \begin{itemize} \item L'utilisateur envoie une requête \texttt{POST} \item Corps de la requête : un objet JSON \end{itemize} \vspace{.5cm} \begin{minted}{json} { "foo": "bar" } \end{minted} \begin{itemize} \item Adresse : \texttt{http://localhost:8000/polls} \end{itemize} \vspace{.5cm} \begin{minted}{ts} router.post("/polls", async (ctx) => { const createPollRequest = await ctx.request.body.json(); // { "foo": "bar" } }); \end{minted} \end{frame} \begin{frame}[fragile]{Routage avec Oak : réponse à la requête} \begin{minted}{ts} export interface APISuccess { success: true; data: T; error?: never; } export interface APIFailure { success: false; data?: never; error: APIError; } \end{minted} \end{frame} \begin{frame}[fragile]{Routage avec Oak : réponse à la requête} \begin{minted}{ts} export enum APIErrorCode { NOT_FOUND = "NOT_FOUND", SERVER_ERROR = "SERVER_ERROR", TIMEOUT = "TIMEOUT", UNAUTHORIZED = "UNAUTHORIZED", VALIDATION_ERROR = "VALIDATION_ERROR", } export interface APIError { code: APIErrorCode; message: string; } \end{minted} \end{frame} \begin{frame}[fragile]{Routage avec Oak : réponse à la requête} \begin{itemize} \item Et finalement, le type de \textbf{toute} réponse de l'API... \emoji{drum}\emoji{drum}\emoji{drum} \end{itemize} \vspace{1.5cm} \pause \begin{minted}{ts} export type APIResponse = APISuccess | APIFailure; \end{minted} \end{frame} \begin{frame}[fragile]{Routage avec Oak : réponse à la requête} \begin{itemize} \item Si l'entité demandée est trouvée : \end{itemize} \vspace{.5cm} \begin{minted}{ts} const responseBody: APIResponse = { success: true, data: poll, }; // Oak passe implicitement le code HTTP OK dans la réponse ctx.response.body = responseBody; \end{minted} \end{frame} \begin{frame}[fragile]{Routage avec Oak : réponse à la requête} \begin{itemize} \item Si l'entité demandée est introuvable : \end{itemize} \vspace{.5cm} \begin{minted}{ts} const responseBody: APIResponse = { success: false, error: { code: APIErrorCode.NOT_FOUND, message: "Poll not found", }, }; ctx.response.status = 404; ctx.response.body = responseBody; \end{minted} \end{frame} \begin{frame}[fragile]{Routage avec Oak : réponse à la requête} \begin{itemize} \item Le serveur rencontre une erreur : \end{itemize} \vspace{.5cm} \begin{minted}{ts} const responseBody: APIResponse = { success: false, error: { code: APIErrorCode.SERVER_ERROR, message: "Failed to fetch poll", }, }; ctx.response.status = 500; ctx.response.body = responseBody; \end{minted} \end{frame} \begin{frame}{Routage avec Oak : contexte} Quelques propriétés importantes de l'objet \texttt{ctx: Context}~\footnote{\url{https://jsr.io/@oak/oak/doc/context/~/Context}} : \vspace{.5cm} \begin{itemize} \item \texttt{request: Request} -- An object which contains information about the current request. \item \texttt{response: Response} -- An object which contains information about the response that will be sent when the middleware finishes processing. \item \texttt{state: S} -- The object to pass state to front-end views. This can be typed by supplying the generic state argument when creating a new app. \begin{itemize} \item \textit{On each request/response cycle, the context's state is cloned from the application state. This means changes to the context's .state will be dropped when the request drops, but "defaults" can be applied to the application's state. Changes to the application's state though won't be reflected until the next request in the context's state.} \end{itemize} \end{itemize} \end{frame} \begin{frame}{Middleware} \begin{itemize} \item Code applicatif qui se positionne \textbf{entre la requête et la réponse} \item \textbf{Intercepte} les données pour leur appliquer des modifications ou effectuer des effets de bord \item En pratique : \begin{itemize} \item Une \textbf{fonction} qui prend en paramètre le \textbf{contexte} (\texttt{ctx}) et la \textbf{prochaine fonction} (\texttt{next}) \item Invoquée dans une \textbf{chaîne} de traitement, ordonnée, pour chaque requête \item Capable d'observer, modifier, court-circuiter ou ignorer le cycle de vie requête-réponse \item Appelée par le framework (Oak) lors de la réception d'une requête \end{itemize} \end{itemize} \end{frame} \begin{frame}[fragile]{Middleware} \begin{minted}[fontsize=\small]{ts} interface HelloWorldState { hello?: string; world?: string; } async function hello(ctx: Context, next: Next) { ctx.state.hello = "Hello"; await next(); } async function world(ctx: Context, next: Next) { ctx.state.world = "world"; await next(); } const router = new Router(); router.get("/hello", hello, world, (ctx) => { ctx.response.body = `${ctx.state.hello}, ${ctx.state.world}!`; }); const app = new Application(); \end{minted} \end{frame} \begin{frame}[fragile]{Exercice 6 : gestion des erreurs du serveur} \begin{block}{Un unique point de sortie pour les réponses d'erreur} On veut éviter de dupliquer du code pour retourner des erreurs au client dans les fonctions de nos routes. On va donc utiliser un \textbf{middleware} qui sera chargé de remplir la réponse en cas d'erreur. \end{block} \begin{minted}{ts} export async function errorMiddleware(ctx: Context, next: Next) { } \end{minted} \begin{alertblock}{Questions} \begin{enumerate} \item Sur quel mécanisme s'appuyer pour intercepter les erreurs levées dans les routes de l'application ? \item Comment différencier une erreur \textit{prévue} d'une erreur \textit{inattendue} ? \end{enumerate} \end{alertblock} \end{frame} \subsection{Gestion de l'état} \begin{frame}[fragile]{Base de données} \begin{columns} \column{.8\textwidth} \begin{itemize} \item 2000 : création de SQLite par D. Richard Hipp \item Objectifs initiaux : base de données relationnelle \textbf{légère} et \textbf{sans serveur} pour être \textbf{embarquée} dans les applications \item Caractéristiques : \begin{itemize} \item \textit{In-memory} : les opérations sont réalisées dans la RAM (pas sur le disque) \item \textit{Single-file} : pas de processus serveur, pas de runtime, pas de dépendances, etc. \end{itemize} \item Simple à déployer, simple à maintenir \item Compétitive en matière de fonctionnalités (transactions, concurrence des accès en lecture/écriture, extensible, etc.) \end{itemize} \vspace{.5cm} \begin{minted}{sh} sqlite3 database.db < schema.sql \end{minted} \column{.2\textwidth} \includegraphics[width=\columnwidth]{img/sqlite.png} \end{columns} \end{frame} \begin{frame}[fragile]{Base de données : rappels sur SQL} \begin{minted}{sql} CREATE TABLE things ( id TEXT PRIMARY KEY, name TEXT NOT NULL, maybe TEXT, active INTEGER NOT NULL ); CREATE TABLE thing_options ( id TEXT PRIMARY KEY, thing_id TEXT NOT NULL, value TEXT NOT NULL, FOREIGN KEY (thing_id) REFERENCES things(id) ON DELETE CASCADE ); \end{minted} \end{frame} \begin{frame}[fragile]{Base de données : transactions} \begin{itemize} \item \textbf{Interdépendance} (sémantique et/ou fonctionnelle) entre deux requêtes \textbf{distinctes} \item Les deux doivent réussir ou échouer \textbf{ensemble} pour éviter une \textbf{incohérence dans l'état} de l'application \end{itemize} \vspace{.5cm} \begin{minted}[fontsize=\footnotesize]{ts} // Enregistrer le vote const voteId = crypto.randomUUID(); const now = new Date().toISOString(); db.prepare( "INSERT INTO votes " + "(id, poll_id, option_id, user_id, created_at) " + "VALUES (?, ?, ?, ?, ?);", ).run(voteId, pollId, optionId, userId || null, now); // Incrémenter le compteur db.prepare( "UPDATE poll_options SET vote_count = vote_count + 1 WHERE id = ?;", ).run(optionId); \end{minted} \end{frame} \begin{frame}[fragile]{Base de données : transactions} \begin{itemize} \item Mécanisme : \textbf{transactions} \item Unités de travail qui garantissent l'\textbf{atomicité} des opérations \end{itemize} \vspace{.5cm} \begin{minted}{ts} try { db.prepare("BEGIN").run(); // Début de transaction db.prepare("...").run(); // Requête 1 db.prepare("...").run(); // Requête 2 db.prepare("COMMIT").run(); // Transaction validée } catch { db.prepare("ROLLBACK").run(); // Transaction annulée } \end{minted} \end{frame} \begin{frame}{Exercice 7 : gestion des erreurs du serveur} \begin{alertblock}{Besoin d'atomicité} \begin{enumerate} \item Quelles opérations doivent être \textbf{atomiques} dans notre application de sondage ? \item Pourquoi ? C'est-à-dire : quels problèmes peuvent survenir si les sous-opérations, disjointes, échouent ? \end{enumerate} \end{alertblock} \end{frame} \subsection{Architecture} \begin{frame}{Organisation du dépôt} \begin{center} \includegraphics[width=.5\textwidth]{img/point.png} \end{center} \end{frame} \begin{frame}[fragile]{Exercice 8 : architecture du serveur} \begin{block}{Organisation des fichiers de l'application} Oak ne privilégie pas de paradigme précis, la responsabilité incombe aux développeurs d'organiser le code de leur application. À ce stade, les composants principaux sont : \begin{itemize} \item Routes \item Middleware \item Interfaces \item Base de données \end{itemize} \end{block} \begin{alertblock}{Questions} \begin{enumerate} \item Doit-on privilégier une organisation type \texttt{module/module.routes.ts} ? \texttt{routes/module.ts} ? Donner des avantages et des inconvénients. \item Pour les deux cas, montrer les directives \texttt{export} et \texttt{import} associées. \end{enumerate} \end{alertblock} \end{frame} \subsection{Fiabilité et tests} \begin{frame}[fragile]{Tests} \begin{minted}{ts} import { assertEquals, assert } from "@std/assert"; const BASE_URL = `http://localhost:8000`; Deno.test({ name: "polls management API", async fn() { const listRes = await fetch(`${BASE_URL}/polls`); assertEquals(listRes.status, 200); const listBody = await listRes.json(); assert(listBody.success); assert(listBody.data.length >= 1); }, }); \end{minted} \end{frame} \begin{frame}{Exercice 9 : un premier jeu de tests} \begin{block}{Organisation des fichiers de l'application} On souhaite éviter toute \textbf{régression} dans le comportement du serveur. Il faut donc tester \textbf{unitairement} les fonctionnalités de base de l'API. \end{block} \begin{alertblock}{Questions} \begin{enumerate} \item Écrire un test pour vérifier le fonctionnement nominal de : \begin{itemize} \item la fonction de vérification de l'interface d'un enregistrement issu de la base de données (\texttt{PollRow}) ; \item la fonction de conversion de cet enregistrement en objet de l'API (\texttt{Poll}) ; \end{itemize} \item Écrire un test pour vérifier le fonctionnement nominal de la route \texttt{/polls}. \end{enumerate} \end{alertblock} \end{frame} \section{Client web} \begin{frame}{HTTP, encore} \begin{itemize} \item API \texttt{fetch} \end{itemize} \end{frame} \begin{frame}[fragile]{Un client web minimal avec TypeScript} \begin{minted}{js} const foo = "bar"; \end{minted} \end{frame} \begin{frame}{Framework : React} \begin{itemize} \item Hooks \begin{itemize} \item \texttt{useParams} \item \texttt{useState} \item \texttt{useRef} \item \texttt{useEffect} \end{itemize} \item Functional Components \item React Router \end{itemize} \end{frame} \begin{frame}{Bundling} \begin{itemize} \item Bundler \item Vite \end{itemize} \end{frame} \begin{frame}{React : architecture} \begin{itemize} \item Service Layer Pattern \end{itemize} \end{frame} \begin{frame}{Bundler} \end{frame} \section{Authentification} \begin{frame}{Concepts généraux} \end{frame} \begin{frame}{\textit{Stateful} : Sessions} \end{frame} \begin{frame}{\textit{Stateless} : JWT} \end{frame} \section{Interactions} \begin{frame}{Data Transfer Objects} \end{frame} \begin{frame}{WebSockets} \end{frame} \section{Déploiement de l'application} \begin{frame}{Nom d'hôte, port, adresse} \begin{center} \texttt{% {\color{BrickRed}https}://% {\color{MidnightBlue}foo.bar}.% {\color{Orchid}com}% [:{\color{LimeGreen}443}]/% {\color{YellowOrange}baz.html}% } \end{center} \begin{center} \includegraphics[width=.5\textwidth]{img/brace.png} \end{center} \begin{itemize} \item \texttt{{\color{BrickRed}https}} : protocole \item \texttt{{\color{MidnightBlue}foo.bar}} : nom d'hôte (\textit{hostname}) \item \texttt{{\color{Orchid}com}} : \textit{TLD} (\textit{Top-Level Domain}) \item \texttt{{\color{LimeGreen}443}} : port (HTTP : 80, HTTPS : 443) \item \texttt{{\color{YellowOrange}baz.html}} : nom de fichier \end{itemize} \end{frame} \begin{frame}{Nom d'hôte, port, adresse} \begin{center} \texttt{% {\color{BrickRed}https}://% {\color{MidnightBlue}foo.bar}.% {\color{Orchid}com}% [:{\color{LimeGreen}443}]/% {\color{YellowOrange}baz.html}% } \end{center} \end{frame} \begin{frame}[fragile]{Reverse proxy} \begin{minted}[fontsize=\footnotesize]{text} http { server { listen 80; server_name coucou.localhost; location / { proxy_pass http://127.0.0.1:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } } } \end{minted} \end{frame} \begin{frame}{Certificats SSL} \begin{itemize} \item Protocole (TLSv1.2, TLSv1.3) \item Algorithme de chiffrement : AES 128/256, etc. \end{itemize} \end{frame} \begin{frame}{CORS} TODO: \end{frame} \section{Débuggage et profilage} \begin{frame}{\textit{What could go wrong...}} \begin{itemize} \item Niveau client \item Niveau serveur \item Niveau base de données \item Interactions API / BDD \item Infrastructure \item Sécurité \item Déploiement \item Gestion de l'état \end{itemize} \end{frame} \subsection{Outillage} \begin{frame}{\textit{Logging}} TODO: \end{frame} \subsection{Profilage des performances} \begin{frame}{Côté serveur} \begin{columns} \column{.5\textwidth} \begin{itemize} \item \texttt{deno run --v8-flags="--prof" main.ts} \begin{itemize} \item Crée un fichier .log dans le répertoire courant \item \textit{"V8 has built-in sample-based profiling. Profiling is turned off by default, but can be enabled via the \texttt{--prof} command-line option. The sampler records stacks of both JavaScript and C/C++ code."} \end{itemize} \item Lecture du fichier avec cpupro~\footnote[frame]{\url{https://discoveryjs.github.io/cpupro/}} \begin{itemize} \item Visualisation : \textit{flame graph}~\footnote[frame]{\fullcite{greggFlameGraph2016}} \end{itemize} \end{itemize} \column{.5\textwidth} \includegraphics[width=\columnwidth]{img/flamegraph.png} \end{columns} \end{frame} \begin{frame}{Côté client} \end{frame} \appendix \section{Bibliographie} \begin{frame}[allowframebreaks]{Références} \nocite{*} \printbibliography[heading=none] \end{frame} % \appendix % \section{Annexes} % \begin{frame}{Annexe 1 -- ...} % \end{frame} \end{document}