diff --git a/Makefile b/Makefile index b0e3a74..fd31982 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,17 @@ BUILD_DIR=build +DOT_FILES=$(wildcard figures/*.dot) +PDF_FILES=$(DOT_FILES:figures/%.dot=$(BUILD_DIR)/figures/%.pdf) + build: $(BUILD_DIR)/report.pdf .PHONY: build -$(BUILD_DIR)/report.aux: report.tex references.bib +$(PDF_FILES):$(BUILD_DIR)/figures/%.pdf: figures/%.dot + @mkdir -p $(BUILD_DIR)/figures + @dot -Tpdf -o $@ $^ -Nfontname='Source Sans Pro' + +$(BUILD_DIR)/report.aux: report.tex references.bib $(PDF_FILES) @mkdir -p $(BUILD_DIR) @xelatex -interaction=nonstopmode -halt-on-error -shell-escape -output-directory=$(BUILD_DIR) report.tex diff --git a/figures/nestjs1.dot b/figures/nestjs1.dot new file mode 100644 index 0000000..e2e4a72 --- /dev/null +++ b/figures/nestjs1.dot @@ -0,0 +1,12 @@ +digraph nestjs1 { + rankdir=LR; + + ChildService [color=dimgrey,fontcolor=dimgrey] + ParentService -> ChildService [color=dimgrey] + + subgraph cluster_ParentModule { + ChildService; ParentService + label="ParentModule" + style=dotted; + } +} diff --git a/figures/nestjs2.dot b/figures/nestjs2.dot new file mode 100644 index 0000000..7593c7c --- /dev/null +++ b/figures/nestjs2.dot @@ -0,0 +1,20 @@ +digraph nestjs1 { + rankdir=LR + + ChildService [color=dimgrey,fontcolor=dimgrey] + ParentService -> ChildService [color=dimgrey] + + subgraph cluster_ParentModule { + ChildService; ParentService + label="ParentModule" + style=dotted + } + + MyService -> ParentService + + subgraph cluster_MyModule { + MyService + label="MyModule" + style=dotted + } +} diff --git a/figures/nestjs3.dot b/figures/nestjs3.dot new file mode 100644 index 0000000..e898d7f --- /dev/null +++ b/figures/nestjs3.dot @@ -0,0 +1,29 @@ +digraph nestjs1 { + rankdir=LR + + Controleur [shape="rectangle"] + Controleur -> Service -> { S1 S2 S3 Sn } + Sn [label="...", color="dimgrey"] + + subgraph cluster_Module { + Controleur; Service + + label="Module" + style=dotted + } + + subgraph cluster_S1 { + S1 + style=dotted + } + + subgraph cluster_S2 { + S2 + style=dotted + } + + subgraph cluster_S3 { + S3 + style=dotted + } +} diff --git a/references.bib b/references.bib index ae760db..994a747 100644 --- a/references.bib +++ b/references.bib @@ -17,7 +17,16 @@ @misc{nestjs, author={NestJS}, title={NestJS - A progressive Node.JS framework}, - howpublished="\url{https://nextjs.com/}", + howpublished="\url{https://nestjs.com/}", year=2023, note="[En ligne; accédé le 31 Janvier 2023]" } + +@inproceedings{yang2008empirical, + title={An empirical study into use of dependency injection in java}, + author={Yang, Hong Yul and Tempero, Ewan and Melton, Hayden}, + booktitle={19th Australian Conference on Software Engineering (aswec 2008)}, + pages={239--247}, + year={2008}, + organization={IEEE} +} diff --git a/report.tex b/report.tex index 8e83715..6cdbd53 100644 --- a/report.tex +++ b/report.tex @@ -9,9 +9,7 @@ \usepackage{mathabx} \usepackage{listings} \usepackage{xcolor} -% \usepackage{subcaption} -% \usepackage{multirow} -% \usepackage{makecell} +\usepackage{float} \usepackage{cite} \usepackage{hyperref} @@ -76,6 +74,8 @@ language=JavaScript } +\graphicspath{{build/figures/}} + \newfontfamily{\Tahoma}{Tahoma} \newfontfamily{\SourceSans}{Source Sans Pro} % Looks like the font from google fonts has a different case, @@ -91,6 +91,7 @@ \newcommand{\entityb}[1]{#1} \newcommand{\person}[2]{#1 #2} \newcommand{\term}[1]{\textit{#1}} +\newcommand{\reffig}[1]{[Figure~\ref{#1}]} \title{Rapport de stage ST40 - A2022} \author{Adrien Burgun} @@ -286,114 +287,140 @@ Durant ce stage, j'ai surtout travaillé sur les applications suivantes: \subsection{Mint Service} -Mint Service est une application construite avec \term{NestJS}. +Mint Service est une application construite avec \entity{NestJS}. NestJS est un framework permettant de construire des applications côté serveur en Javascript, et de s'assurer que ces applications soient à la fois sûres, efficaces et flexibles \cite{nestjs}. -\subsubsection{NestJS et l'organisation du code dans Mint Service} +\subsubsection{NestJS et organisation du code} % TODO: hiérarchiser l'argumentaire -NestJS emploie à son coeur les principes de \term{modularité} et d'\term{inversion de contrôle} (\term{IoC}). +NestJS emploie à son coeur les principes de \term{modularité} et d'\term{inversion de contrôle} (\term{IoC}): +\\ +L'application est séparée en différents modules, qui eux-mêmes contiennent des \term{services} et des \term{controleurs}. \reffig{nestjs3} -L'application est séparée en différents modules, permettant de faire une séparation claire entre les morceaux de codes utilisés -pour répondre à différents besoins. +\begin{description} + \item[Les services] (aussi appelés \term{providers}) contiennent l'implémentation de la logique nécessaire + pour répondre aux besoins de l'application: contacter une base de donnée, traiter l'information, faire des + requêtes réseau, etc. + + \item[Les controleurs] sont responsables d'accepter les requêtes des \entity{clients}, de les transformer, de vérifier + leur légitimité et d'appeler les méthodes des \entity{services} avant de transformer la réponse à envoyer au \entity{client}. + \\ + NestJS fournit un ensemble d'outils simplifiant l'implémentation des controleurs. + + \item[Les modules] s'occupent de gérer la configuration des \entity{services} et des \entity{controleurs}: + chaque \entity{module} indique à NestJS quels \entity{services} et \entity{controleurs} il contient, + ainsi que comment NestJS doit construire ces services dans les cas plus complexes. + \\ + Les modules peuvent exporter un sous-ensemble de leurs services, ce qui permet de rapidement + réutiliser des services en important le module leur étant associé. \reffig{nestjs2} +\end{description} -Chaque module contient des \term{services}, aussi appelés \term{providers}, qui contiennent du code métier -(contacter la base de donnée, traiter de l'information, etc). -Les modules ont également des \term{controlleurs}, qui permettent de définir les différents chemins qui seront exposés -sur le réseau. -Un contrôleur est responsable pour transformer les requêtes, vérifier leur légitimité, puis il appele les méthodes -d'un ou plusieurs services avant de transformer la réponse puis l'envoyer au client. +\begin{figure}[H] + \centering + \includegraphics{nestjs3} + \caption{Organisation d'un module NestJS typique} + \label{nestjs3} +\end{figure} -% TODO: trouver une source -La séparation entre \term{contrôleur} et \term{service} permet d'avoir une claire \term{séparation des préoccupations}: -les contrôleurs s'occupent de gérer, valider et répondre aux requêtes faites par un client, -tandis que les services s'occupent de l'implémentation de la logique permettant de satisfaire la requête. -Il devient également plus simple avec ce système de modifier l'implémentation de la logique côté service -sans mettre en danger la fonctionnalité du service avec d'autres applications: la gestion et la vérification -des requêtes restant inchangée. +La séparation entre controleurs et services permet d'avoir une claire \term{séparation des préoccupations}: -Pour limiter le couplage entre les services, et pour simplifier la réutilisation des services, -NestJS permet de faire de l'\term{injection de dépendances} dans les services et dans les contrôleurs, -d'une manière similaire framework \entity{Spring} en Java. +\begin{itemize} + \item Les controleurs s'occupent de gérer, valider et répondre aux requêtes faites par un client; + \item Les services s'occupent de l'implémentation de la logique permettant de satisfaire la requête; + \item Les modules s'occupent de configurer les services et les controleurs, et permettent la modularisation du code. +\end{itemize} -Par exemple, si un service \textit{ParentService} souhaite utiliser des méthodes d'un service \textit{ChildService}, -alors au lieu de créer une instance de \textit{ChildService} dans \textit{ParentService}, \textit{ParentService} -indique qu'il aimerait recevoir une instance de \textit{ChildService}, et l'utilisateur de \textit{ParentService} -est responsable pour fournir à celui-ci une instance de \textit{ChildService}. +Pour limiter le couplage entre les services, et pour simplifier la réutilisation des services, +NestJS permet de faire de l'\term{injection de dépendances} par \term{constructeur sans défaut} \cite{yang2008empirical} dans les services et dans les controleurs, +d'une manière similaire au framework \entity{Spring} en \entity{Java}. \reffig{nestjs1} -NestJS simplifie ce processus, en permettant aux modules de faire la liste des services disponibles, -et en détectant automatiquement quels services dépendent de quels services à partir des arguments du constructeur. -NestJS construit ensuite les instances de ces services et les \term{injecte} en paramètre aux services en dépendant. +% TODO: trouver une source +% Separation of concerns +% https://citeseerx.ist.psu.edu/document?repid=rep1&type=pdf&doi=0fd24fa8ac2a77c4709103eb8a179fec38311fe8 +% "On the importance of the separation-of-concerns principle in secure software engineering" +Il devient ainsi plus simple avec ce système de modifier différentes parties du code sans accidentellement casser une autre partie du code. +Il est aussi plus simple de tester du code organisé de la manière suivante, car les services et les controleurs peuvent +être placés dans un environnement isolé et testés individuellement ou collectivement. + +\begin{figure} + \begin{lstlisting}[style=JavaScript] + export abstract class IChildService { + // ... + } -\begin{lstlisting}[style=JavaScript] -export abstract class IChildService { - // ... -} + @Injectable(IChildService) + export class ChildService implements IChildService { + // ... + } -@Injectable(IChildService) -export class ChildService implements IChildService { - // ... -} + @Injectable() + export class ParentService { + private childService: IChildService; -@Injectable() -export class ParentService { - private childService: IChildService; + constructor( + childService: IChildService + ) { + this.childService = IChildService; + } - constructor( - childService: IChildService - ) { - this.childService = IChildService; + // ... } - // ... -} - -@Module({ - providers: [ - ChildService, - ParentService - ], - exports: [ - ParentService - ] -}) -export class ParentModule {} -\end{lstlisting} - -Les modules peuvent enfin exporter un sous-ensemble de ces services, qui peuvent alors être facilement réutilisés -à d'autres endroits de l'application sans avoir à reconfigurer l'ensemble des dépendances de ces services: - -\begin{lstlisting}[style=JavaScript] -@Injectable() -export class MyService { - constructor( - parentService: ParentService - ) { - // ... + @Module({ + providers: [ + ChildService, + ParentService + ], + exports: [ + ParentService + ] + }) + export class ParentModule {} + \end{lstlisting} + + \centering + \includegraphics{nestjs1} + \caption{Exemple d'injection de dépendances avec NestJS} + \label{nestjs1} +\end{figure} + +\begin{figure} + \begin{lstlisting}[style=JavaScript] + @Injectable() + export class MyService { + constructor( + parentService: ParentService + ) { + // ... + } } -} -@Module({ - imports: [ - // Comme ParentModule exporte ParentService, - // ce service est facilement rendu disponible ici pour MyService: - ParentModule - ], - providers: [ - MyService - ] -}) -export class MyModule {} -\end{lstlisting} + @Module({ + imports: [ + // Comme ParentModule exporte ParentService, + // ce service est facilement rendu disponible ici pour MyService: + ParentModule + ], + providers: [ + MyService + ] + }) + export class MyModule {} + \end{lstlisting} + + \centering + \includegraphics{nestjs2} + \caption{Exemple d'import de dépendances avec NestJS (suite de \ref{nestjs1})} + \label{nestjs2} +\end{figure} \newpage \bibliographystyle{plain} \bibliography{references}{} - \makeutbmbackcover{} \end{document}