
RésaResto
Présentation du projet
L'organisation d'un repas en groupe, qu'il s'agisse d'un déjeuner entre collègues ou d'une sortie entre amis, peut rapidement devenir un casse-tête. Choisir une date qui convienne à tout le monde, trouver un lieu adapté et s'assurer que chacun puisse s'organiser sont autant de défis qui compliquent la planification. RésaResto est un projet personnel que j'ai développé pour répondre à cette problématique, en rendant le processus plus fluide et plus accessible.
L'idée de cette application est née au cours de mon stage, où j'ai constaté que planifier un repas collectif pendant les journées en présentiel était souvent une source de confusion. Entre les échanges dispersés sur différentes plateformes, les incertitudes quant à la disponibilité de chacun et l'absence d'un outil centralisé, il était fréquent que l'organisation prenne plus de temps que prévu, voire qu'elle échoue complètement. Afin d'améliorer cette dynamique et de perfectionner mes compétences en développement, j'ai entrepris la conception de RésaResto, une application permettant de proposer des lieux et des horaires de repas, d'y répondre facilement et de visualiser en temps réel les choix des participants.
Ce projet ne se limite pas à une simple application de planification, mais vise à offrir une solution intuitive et collaborative pour simplifier la prise de décision collective. En centralisant toutes les informations essentielles sur une interface unique, RésaResto permet à chacun de proposer une sortie, de consulter les propositions en cours et de rejoindre celles qui l'intéressent en quelques clics. Cette approche favorise une organisation plus fluide, réduit les échanges répétitifs et assure une meilleure coordination entre les participants.
Objectifs
L’objectif principal de cette réalisation est de mettre en place un système structuré de gestion des propositions de restaurants afin de simplifier l’organisation des repas en groupe. RésaResto vise à éviter les discussions éparpillées et les prises de décisions laborieuses en offrant un espace centralisé où les utilisateurs peuvent proposer un restaurant, consulter les propositions existantes et voter en quelques clics.
Le projet s’inscrit également dans une démarche d’apprentissage et de mise en pratique de compétences full-stack, en exploitant Flutter pour le front-end et C# .NET pour le back-end. L’enjeu est d’assurer une interaction fluide entre les différentes couches de l’application, tout en garantissant une expérience utilisateur intuitive et un accès rapide aux informations.
Enfin, l’intégration d’un système de gestion des groupes permet d’aller au-delà d’une simple liste de propositions individuelles. Chaque utilisateur peut créer ou rejoindre un groupe et y soumettre des suggestions de restaurants, améliorant ainsi l’engagement et la participation.
Enjeux de la réalisation
L’un des principaux enjeux de RésaResto réside dans son ergonomie et son adoption par les utilisateurs. L’application doit être intuitive et rapide à prendre en main, sous peine de ne pas être utilisée. Une navigation trop complexe ou un manque de clarté dans l’affichage des propositions pourraient freiner son adoption.
La réactivité et la fluidité du service constituent un autre défi majeur. L’actualisation des propositions et des votes doit être instantanée pour garantir une interaction en temps réel entre les utilisateurs. Toute latence ou problème de synchronisation risquerait de rendre l’application inefficace et frustrante à utiliser.
L’application doit également être évolutive. Si le nombre d’utilisateurs augmente, la gestion des ressources doit rester fluide pour éviter les ralentissements. Pour répondre à cet enjeu, on a misé sur une architecture modulaire, avec un déploiement conteneurisé via Docker, orchestré ensuite par Kubernetes et automatisé grâce à ArgoCD. Ce trio technique permet d’accompagner une montée en charge progressive, tout en maintenant de bonnes performances et une stabilité continue.
Enfin, la gestion des accès et la sécurisation des données sont des aspects clés du projet. RésaResto doit garantir que seuls les utilisateurs autorisés puissent créer et modifier des propositions au sein de leurs groupes. Une intégration future de Keycloak est envisagée pour renforcer la gestion des authentifications et des droits d’accès.
Risques et contraintes
L’un des principaux risques réside dans la complexité technique liée à l’interaction entre le front-end et le back-end. La gestion des requêtes et des réponses entre Flutter et C# .NET doit être optimisée pour éviter des problèmes de performance ou d’incohérence dans l’affichage des données. Une mauvaise structuration du code pourrait également rendre les évolutions futures difficiles.
La gestion des erreurs et des requêtes API est un autre point critique. Étant donné que l’application repose sur des échanges en temps réel avec le serveur, toute interruption ou latence excessive doit être anticipée et gérée proprement pour éviter des dysfonctionnements visibles côté utilisateur.
Un risque majeur concerne également l’adoption par les utilisateurs. Si l’application ne répond pas précisément aux besoins ou manque de certaines fonctionnalités essentielles, elle risque d’être abandonnée. Il est donc crucial de prendre en compte les retours et d’itérer rapidement pour améliorer l’expérience proposée.
Enfin, l’optimisation des coûts d’infrastructure est un facteur à surveiller. Le déploiement sous Kubernetes apporte une grande flexibilité, mais une mauvaise gestion des ressources pourrait engendrer des coûts excessifs. Un suivi rigoureux est nécessaire pour assurer un équilibre entre performance et maîtrise des ressources.
Ma position dans le projet
Étant seul sur le développement de RésaResto, j'ai dû gérer tous les aspects du projet, de la conception à la mise en ligne. J'ai assuré à la fois l'analyse du besoin, le développement full-stack, ainsi que le déploiement et l'optimisation de l'application. Cette autonomie m'a permis de prendre toutes les décisions techniques tout en m'imposant une gestion rigoureuse du projet. Ce défi m'a offert une vision complète du cycle de développement et m'a poussé à renforcer ma capacité d'organisation et d'adaptation.
Mise en œuvre
Bien que j'aie pris en charge l'ensemble des aspects du projet, j'ai fait le choix, pour une meilleure clarté et une présentation plus pertinente, de me concentrer sur une fonctionnalité clé de l'application : les propositions de restaurants. Cette fonctionnalité représente le cœur de l'expérience utilisateur, car elle répond directement à l'objectif initial du projet.
Information
Avant d’aborder cette réalisation, il est important de souligner qu’elle m’a permis d’approfondir et de mettre en pratique plusieurs compétences techniques et humaines. Certaines d’entre elles sont détaillées dans d’autres sections de ce portfolio. Si certaines explications vous semblent trop succinctes, je vous invite à vous y référer pour une meilleure compréhension.
Première tâche - Maquettage
La première étape de conception de RésaResto a consisté à réaliser un maquettage des interfaces afin de définir l'organisation des éléments visuels et l'expérience utilisateur. Cette phase m’a permis de structurer l’application avant d’entrer dans le développement, en garantissant une base claire et solide pour la suite du projet.
Contrairement à une approche numérique avec des outils comme Figma, j’ai choisi de faire mes maquettes au papier et crayon. Cette méthode m’a permis de poser rapidement différentes idées de disposition, sans contrainte logicielle, et de rester concentré sur les fonctionnalités essentielles. L’objectif était avant tout d’avoir une vision simple, mais efficace, de ce que devait proposer l’application dès le premier écran.
Ce choix reflète aussi une décision de gestion de projet : en travaillant seul, il faut savoir prioriser ce qui est vraiment utile à chaque étape. J’ai donc volontairement limité le temps passé sur la partie design, pour avancer plus rapidement vers l’implémentation technique. Malgré ce format simple, les maquettes ont été un vrai support pour structurer l’application et garder une vision globale tout au long du développement.
Maquette - Formulaire connexion et inscription
Cette première maquette représente l’écran d’authentification, qui regroupe à la fois la connexion et l’inscription selon le contexte. L’interface est volontairement centrée et épurée, avec un formulaire simple et un lien de redirection vers l’autre action. L’objectif était ici d’aller droit au but pour faciliter l’accès à l’application, tout en mettant en valeur le nom de l’application et l’identité visuelle.

Ce type d’écran a été pensé pour offrir une expérience rapide et intuitive, évitant les distractions ou les hésitations dès le premier contact avec l’application. Il pose les bases d’une navigation claire, dans un style épuré, en cohérence avec la suite du projet.
Maquette - Accueil
La deuxième maquette correspond à la page d’accueil accessible après connexion. Elle contient le formulaire de proposition de repas (avec lieu et date) ainsi qu’un carrousel d’avis utilisateurs sur les restaurants. En haut à droite, on retrouve les icônes de profil, déconnexion, et l’accès au panel admin pour les utilisateurs ayant les droits.

Cet écran est central dans l’usage de l’application. Il regroupe les actions principales pour l’utilisateur tout en proposant une navigation rapide. L’interface a été pensée pour être fonctionnelle et évolutive, en prévoyant notamment les redirections vers la liste de restaurants correspondant aux informations renseignées dans le formulaire.
Maquette - Profil utilisateur
Enfin, la dernière maquette illustre l’espace profil de l’utilisateur. Cet écran permet à l’utilisateur de consulter ou modifier ses informations personnelles, tout en offrant une passerelle vers l’interface d’administration si son statut le permet.

Même si cette page est secondaire dans le parcours principal, elle reste essentielle dans la gestion de l’identité utilisateur et dans l’accès aux fonctions avancées. Elle permet aussi d’assurer une expérience unifiée entre les différents profils (utilisateur simple ou administrateur), sans complexifier l’interface.
Deuxième tâche - Back-end
La mise en place du back-end pour RésaResto repose sur plusieurs composants essentiels : la définition des entités de base de données (DTO), l’implémentation des migrations avec Entity Framework Core, et la création des routes API REST pour permettre l’interaction avec le système. Ce développement s’appuie sur C# .NET, un langage robuste et performant, particulièrement adapté aux applications Web et aux services back-end.
Modélisation des données
La base de données repose sur un ensemble d’entités interconnectées, conçues pour refléter la logique métier de l’application. Une proposition de repas est toujours initiée par un utilisateur, qui en est le créateur. Sa diffusion et son suivi sont ensuite gérés à travers un groupe d’utilisateurs, auquel elle est rattachée. Grâce à cette organisation, les rôles de créateur et de participant restent bien distincts, et la gestion des interactions est centralisée au niveau des groupes. Cela permet d’afficher automatiquement les propositions pertinentes en fonction de l’appartenance à un groupe, tout en assurant une structure claire et évolutive pour le projet.
Ce modèle permet d’assurer une séparation nette entre les rôles de créateur et de participant, tout en centralisant les interactions dans les groupes. Il facilite également l’automatisation de l’affichage des propositions pertinentes, assurant ainsi une meilleure personnalisation de l’expérience utilisateur.
L’implémentation de ces entités repose sur Entity Framework Core, avec des annotations facilitant la génération des relations et des contraintes en base.
Entités - Proposition
public class Proposition
{
public int Id { get; set; }
public DateOnly? Date { get; set; }
public TimeOnly? EndingVote { get; set; }
public int RestaurantId { get; set; }
public int UtilisateurId { get; set; }
public int GroupeId { get; set; }
public Groupe Groupe { get; set; } = null!;
public List<PropositionsUtilisateurs> UsersOk { get; set; } = null!;
}
Le champ
UtilisateurIddésigne l’utilisateur à l’origine de la proposition, tandis queGroupeIdindique le groupe auquel cette proposition est rattachée.:::
Entités - PropositionsUtilisateurs
public class PropositionsUtilisateurs
{
public int Id { get; set; }
public int PropositionId { get; set; }
public Proposition Proposition { get; set; } = null!;
public int UtilisateurId { get; set; }
public Utilisateur Utilisateur { get; set; } = null!;
}
Une entité de liaison enregistre les réponses des utilisateurs aux propositions liées à leurs groupes.
Entités - Utilisateur
public class Utilisateur
{
public int Id { get; set; }
public string Nom { get; set; } = null!;
public string Prenom { get; set; } = null!;
public string Mail { get; set; } = null!;
public string Pass { get; set; } = null!;
public string? Image { get; set; }
public Role LevelAccess { get; set; }
public List<GroupeUtilisateurs> GroupeUtilisateurs { get; set; } = null!;
}
L’entité
Utilisateurconserve les informations personnelles et assure la gestion des rôles et des appartenances aux groupes, ce qui conditionne sa visibilité sur les propositions.
Génération des migrations
L’un des avantages majeurs d’Entity Framework Core réside dans sa capacité à automatiser la création et l’évolution de la base de données grâce aux migrations. Cette approche permet de générer le schéma relationnel directement à partir des entités C#, en assurant une parfaite cohérence entre le modèle objet et la structure physique de la base.
Chaque modification du modèle, qu’il s’agisse d’ajouter une propriété, d’introduire une relation entre entités ou d’ajuster une contrainte, peut être traduite en migration. Cela permet de suivre précisément l’évolution du schéma de base de données au fil du développement, tout en conservant un historique des changements.
Dans le cas de RésaResto, la migration initiale a permis de structurer les entités fondamentales du système, notamment Proposition, en générant automatiquement les tables, colonnes et contraintes de clés étrangères nécessaires à l’intégrité des données.
Migration - Proposition
migrationBuilder.CreateTable(
name: "Propositions",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
Date = table.Column<DateOnly>(nullable: true),
EndingVote = table.Column<TimeOnly>(nullable: true),
RestaurantId = table.Column<int>(nullable: false),
UtilisateurId = table.Column<int>(nullable: false),
GroupeId = table.Column<int>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Propositions", x => x.Id);
table.ForeignKey("FK_Propositions_Utilisateurs_UtilisateurId", x => x.UtilisateurId, "Utilisateurs", "Id", onDelete: ReferentialAction.Cascade);
table.ForeignKey("FK_Propositions_Groupes_GroupeId", x => x.GroupeId, "Groupes", "Id", onDelete: ReferentialAction.Cascade);
});
Cette migration illustre la génération automatique des relations entre entités : chaque proposition est liée à un créateur (
Utilisateur) et à un groupe cible (Groupe), assurant ainsi l’intégrité référentielle du système.
Mise en place des routes API
Pour permettre l’interaction entre l’application et la base de données, j’ai mis en place une série de routes REST en utilisant FastEndpoints, un framework léger et performant de l’écosystème ASP.NET Core. Leur structuration claire garantit une meilleure lisibilité du code et facilite les évolutions futures, notamment l’ajout de filtres, de règles métiers ou de nouvelles extensions.
Ces routes couvrent les principaux besoins fonctionnels liés aux propositions :
- Récupérer les propositions existantes
- Créer une nouvelle proposition
- Modifier une proposition existante
- Supprimer une proposition devenue obsolète ou invalide

Consultation des propositions
Une route GET a été définie pour exposer la liste complète des propositions enregistrées. Cette route est utilisée aussi bien pour afficher l’historique personnel des utilisateurs que pour permettre à un administrateur de consulter l’ensemble des propositions du système.
Endpoint GET - Récupérer toutes les propositions
public override async Task HandleAsync(GetPropositionRequete request, CancellationToken ct)
{
Response = await Context.Propositions
.OrderBy(p => p.Id)
.Select(p => Map.FromEntity(p))
.ToListAsync(ct);
await SendOkAsync(Response, ct);
}
Cette route retourne toutes les propositions triées par ordre croissant d’identifiant. Elle constitue la base d’affichage côté utilisateur ou administrateur.
Création d’une nouvelle proposition
Lorsqu’un utilisateur souhaite soumettre un repas à ses collègues ou amis, il utilise un formulaire déclenchant une requête POST. Cette route reçoit un objet de type PropositionResponse, le transforme en entité métier et l’ajoute en base. Une fois l’enregistrement effectué, la proposition devient immédiatement visible par les autres membres du groupe concerné.
Endpoint POST - Créer une proposition
public override async Task HandleAsync(PropositionResponse request, CancellationToken ct)
{
var newProp = await Map.ToEntityAsync(request);
await Context.Propositions.AddAsync(newProp, ct);
await Context.SaveChangesAsync(ct);
Response = await Map.FromEntityAsync(newProp);
await SendCreatedAtAsync<GetPropositionEndpoint>(new { Response.Id }, Response, cancellation: ct);
}
Ce flux permet une création rapide et fiable de proposition. Grâce au mapping bidirectionnel, les données sont uniformisées à l’entrée comme à la sortie, facilitant leur exploitation côté front-end.
Mise à jour d’une proposition existante
Une route PUT a également été implémentée pour permettre la modification d’une proposition existante. Cette fonctionnalité peut être utilisée en cas de changement d’horaire, de lieu, ou de groupe concerné. L’endpoint accepte un objet mis à jour et l’enregistre en base, en tenant compte de la clé transmise dans l’URL.
Endpoint PUT - Modifier une proposition
public class PutPropositionEndpoint : Endpoint<PropositionResponse, PropositionResponse, PropositionMapper>
{
public MariaDbReferentielsContext Context { get; set; } = null!;
public override void Configure()
{
AllowAnonymous();
Summary(es =>
{
es.Response<PropositionResponse>(StatusCodes.Status201Created);
es.Response(StatusCodes.Status404NotFound);
});
Put("Propositions/{Id}");
}
}
Cette route constitue un levier de flexibilité pour les utilisateurs. Elle permet de garder une base de données à jour sans nécessiter de suppression-recréation, tout en assurant une traçabilité complète des modifications.
Troisième tâche - Front-end
Le développement de la partie front-end de RésaResto a été réalisé avec Flutter, un framework moderne permettant de concevoir des interfaces réactives et multiplateformes. Couplé au langage Dart, il m’a permis de concevoir une application fluide, lisible et bien intégrée au back-end en C# .NET.
Dès le début du projet, j’ai structuré l’interface autour de composants réutilisables, facilitant l’ajout de fonctionnalités et l’évolution de l’architecture visuelle. L’objectif était de proposer une expérience utilisateur intuitive, légère et cohérente, tout en respectant les contraintes techniques posées par la logique back-end.
Cette interface s’articule autour de plusieurs éléments clés : l’affichage des propositions existantes, la création d’une nouvelle proposition, et l’intégration des groupes d’utilisateurs. Chacune de ces briques repose sur des widgets dynamiques, garantissant une interactivité fluide à chaque action.
Définition des modèles de données
Pour assurer une compatibilité parfaite entre les données front-end et back-end, j’ai défini des modèles Dart calqués sur les DTO C#**. Cela garantit une cohérence dans la structure des objets manipulés de part et d’autre de l’application.
Modèle - Proposition
class Proposition {
final int id;
final DateTime date;
final DateTime endingVote;
final int restaurantId;
final int utilisateurId;
final List<int>? usersOk;
Proposition({
required this.id,
required this.date,
required this.endingVote,
required this.restaurantId,
required this.utilisateurId,
this.usersOk,
});
factory Proposition.fromJson(Map<String, dynamic> json) {
return Proposition(
id: json['id'],
date: DateTime.parse(json['date']),
endingVote: DateTime.parse(json['endingVote']),
restaurantId: json['restaurantId'],
utilisateurId: json['utilisateurId'],
usersOk: List<int>.from(json['usersOk'] ?? []),
);
}
Map<String, dynamic> toJson() {
return {
'id': id,
'date': date.toIso8601String(),
'endingVote': endingVote.toIso8601String(),
'restaurantId': restaurantId,
'utilisateurId': utilisateurId,
'usersOk': usersOk ?? [],
};
}
}
Ce modèle permet de représenter une proposition de repas et de la convertir dynamiquement entre format JSON et objet Dart, assurant la liaison entre interface et API REST.
Création d’une proposition
L’ajout d’une nouvelle proposition repose sur un formulaire dynamique, construit avec un widget StatefulWidget. Ce formulaire intègre les champs nécessaires à la création d’une proposition, comme la date, le groupe concerné, et le restaurant ciblé. Les groupes sont récupérés automatiquement via une requête GET à l’API, et affichés dans un menu déroulant mis à jour à l’initialisation du widget.
Interface - Formulaire de création
class PropositionForm extends StatefulWidget {
final Restaurant restaurant;
final String date;
const PropositionForm({required this.restaurant, required this.date});
_PropositionFormState createState() => _PropositionFormState();
}
Ce formulaire met à disposition une interface réactive, connectée aux groupes disponibles, et préremplit les données existantes pour simplifier l'interaction utilisateur.
Communication avec l’API REST
L’envoi des données vers l’API se fait via une requête POST, réalisée avec la bibliothèque HTTP de Flutter. Cette requête déclenche l’enregistrement de la proposition en base de données côté serveur. Le retour de statut est ensuite affiché à l’utilisateur pour valider ou corriger l’opération.
Appel API - Sauvegarde d’une proposition
Future<void> saveProposition(Proposition newProposition, int? idGroup) async {
final apiUrl = Uri.parse('${AppConfig.apiBaseUrl}/Propositions');
try {
final response = await http.post(
apiUrl,
headers: {'X-Apikey': '${AppConfig.apiKey}', 'Content-Type': 'application/json'},
body: jsonEncode(newProposition.toJson()),
);
if (response.statusCode == 201) {
print("Proposition créée avec succès !");
} else {
print("Erreur : ${response.statusCode}");
}
} catch (e) {
print("Erreur de requête : $e");
}
}
Cette fonction assure l’interfaçage entre la logique métier Flutter et le back-end, avec une sérialisation directe du modèle
Propositionvers JSON.
Affichage des propositions existantes
Une fois enregistrées, les propositions sont récupérées via l’API, puis affichées à l’utilisateur sous forme de cartes interactives. Chaque carte présente les informations essentielles : date, restaurant, et nombre de participants. Ce système repose sur un composant réutilisable PropositionCard.
Composant - Affichage d’une proposition
class PropositionCard extends StatelessWidget {
final Proposition proposition;
PropositionCard({required this.proposition});
Widget build(BuildContext context) {
return Card(
child: Column(
children: [
Text("Restaurant : ${proposition.restaurantId}"),
Text("Date : ${proposition.date}"),
Text("Participants : ${proposition.usersOk?.length ?? 0}"),
],
),
);
}
}
Ce composant modulaire garantit une lisibilité claire des données et permet d’unifier l’affichage dans les différentes parties de l’application.
Quatrième tâche - DevOps
Le déploiement de RésaResto repose sur une architecture conteneurisée et modulaire, conçue pour garantir à la fois performance, évolutivité et fiabilité. J’ai utilisé Docker pour encapsuler chaque service de manière isolée, puis Kubernetes pour orchestrer l’ensemble, assurant une répartition dynamique des ressources et une gestion centralisée des composants. Pour automatiser les déploiements et maintenir la cohérence entre le code et l’environnement, j’ai intégré ArgoCD à travers une logique GitOps, reposant sur des fichiers de configuration versionnés dans un dépôt Git.
Ce choix d’infrastructure répond à des exigences précises de scalabilité, de tolérance aux pannes et d’automatisation continue. L’application peut ainsi absorber une montée en charge, être redéployée sans interruption, et s’autoréparer en cas d’écarts entre l’état désiré et l’état réel du cluster. La traçabilité offerte par Git renforce la sécurité opérationnelle tout en simplifiant les itérations et la maintenance.
Organisation de l’infrastructure Kubernetes
L’infrastructure s’articule autour de plusieurs services encapsulés en pods, chacun décrit via des fichiers YAML et déployé dans le cluster via Kubernetes :
- Le back-end ASP.NET Core, exposant l’API REST centrale
- Le front-end Flutter, constituant l’interface utilisateur
- La base de données MariaDB, assurant la persistance des données
- Le système d’ingress, servant de point d’entrée unique pour router les requêtes vers les bons services
Chaque composant repose sur des déploiements indépendants, exposés par des services Kubernetes. Un Ingress Controller permet de mutualiser l’accès aux services et d’assurer un routage performant des appels API ou interfaces utilisateur.
Déploiement des services back-end et front-end
Chaque service de RésaResto est décrit dans un manifeste Kubernetes dédié, précisant son image Docker, ses variables d’environnement, son nombre de réplicas, et ses ports exposés. Cette configuration garantit une modularité totale et facilite le scaling horizontal.
Configuration Kubernetes - Fichier de déploiement back-end
apiVersion: apps/v1
kind: Deployment
metadata:
name: resaresto-backend
spec:
replicas: 2
selector:
matchLabels:
app: resaresto-backend
template:
metadata:
labels:
app: resaresto-backend
spec:
containers:
- name: backend
image: registry.example.com/resaresto-backend:latest
ports:
- containerPort: 5000
env:
- name: ConnectionStrings__Default
value: "Server=mariadb-service;Database=resaresto;User=root;Password=password;"
---
apiVersion: v1
kind: Service
metadata:
name: resaresto-backend
spec:
selector:
app: resaresto-backend
ports:
- protocol: TCP
port: 80
targetPort: 5000
type: ClusterIP
Ce fichier YAML définit le déploiement du back-end avec deux réplicas pour assurer la haute disponibilité. Un service de type
ClusterIPpermet la communication interne avec les autres services du cluster.
Configuration Kubernetes - Fichier de déploiement front-end
apiVersion: apps/v1
kind: Deployment
metadata:
name: resaresto-frontend
spec:
replicas: 2
selector:
matchLabels:
app: resaresto-frontend
template:
metadata:
labels:
app: resaresto-frontend
spec:
containers:
- name: frontend
image: registry.example.com/resaresto-frontend:latest
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: resaresto-frontend
spec:
selector:
app: resaresto-frontend
ports:
- protocol: TCP
port: 80
targetPort: 80
type: LoadBalancer
Le front-end Flutter est exposé à l’extérieur du cluster via un service
LoadBalancer. Deux instances tournent simultanément pour garantir une continuité d’accès à l’interface.
Automatisation du déploiement avec ArgoCD
L’ensemble des services est versionné dans un dépôt Git contenant les fichiers YAML. C’est ArgoCD qui se charge de synchroniser en temps réel ce dépôt avec l’état du cluster. À chaque modification du dépôt, ArgoCD applique automatiquement les nouvelles configurations dans l’environnement cible, garantissant un cycle de déploiement fluide et contrôlé.
Configuration ArgoCD - Fichier Application
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: resaresto
spec:
project: default
source:
repoURL: 'https://github.com/mon-repo/resaresto-k8s'
path: manifests
targetRevision: main
destination:
server: https://kubernetes.default.svc
namespace: resaresto
syncPolicy:
automated:
prune: true
selfHeal: true
Ce fichier décrit l’application ArgoCD et permet une synchronisation automatique et auto-réparatrice, garantissant que le cluster reflète toujours l’état défini dans Git.
Mise en place du pipeline GitOps
L’infrastructure de RésaResto repose sur une logique GitOps, dans laquelle les mises à jour sont automatisées et pilotées depuis le dépôt Git. Ce fonctionnement assure à la fois la traçabilité, la sécurité et la réversibilité des déploiements.
- Le développeur pousse une modification dans le repository Git
- ArgoCD détecte le changement et applique les nouvelles ressources au cluster
- Kubernetes déploie progressivement les nouveaux pods
- En cas d’échec, un rollback automatique est possible pour revenir à l’état précédent
Grâce à cette approche, chaque modification est maîtrisée, chaque déploiement est documenté, et chaque anomalie peut être corrigée rapidement, sans intervention manuelle complexe.
Résultat
À ce jour, RésaResto est pleinement opérationnel en production, utilisé régulièrement pour faciliter l'organisation des repas en groupe. L'application permet aux utilisateurs de proposer des restaurants, d’inviter des groupes et de voter pour une sortie, rendant le processus plus fluide et structuré.
L'ensemble des fonctionnalités développées répond aux objectifs initiaux, assurant une interaction intuitive entre les utilisateurs et une gestion efficace des propositions. Grâce à son intégration avec une API REST et son architecture modulaire, RésaResto offre une expérience fluide et réactive, garantissant un bon équilibre entre performance et accessibilité.
La mise en place du back-end en C# .NET et du front-end en Flutter a permis de structurer une application robuste et évolutive. Son déploiement sous Kubernetes avec ArgoCD assure une mise à l'échelle automatique, une maintenance facilitée et un suivi en temps réel de l’état de l’application.
Avenir du projet
À court terme, une refonte graphique et une optimisation du back-end sont prévues. L’objectif est d’améliorer l’ergonomie de l’application et d’optimiser certaines requêtes pour renforcer la rapidité et l’efficacité des traitements. Cette phase inclura également une amélioration du système de gestion des groupes et des permissions, permettant d’affiner les contrôles d’accès et la gestion des droits utilisateurs.
À plus long terme, plusieurs évolutions sont envisagées :
- Ajout de nouvelles fonctionnalités : filtres de recherche avancés, suggestions intelligentes de restaurants, historique des propositions.
- Connexion avec des OpenAPI externes : intégration avec des services tiers (bases de données de restaurants, systèmes de réservation, API de navigation).
- Mise en place d’un système de monitoring avancé : intégration de Prometheus et Grafana pour une surveillance en temps réel des performances et erreurs applicatives.
- Renforcement de la sécurité avec Keycloak : une authentification plus poussée, avec rôles et permissions affinés, assurant une meilleure protection des données.
Ces améliorations permettront à RésaResto d’évoluer en une plateforme plus complète, plus performante, et capable de s’intégrer dans des contextes variés, qu’ils soient personnels ou professionnels.
Retour sur expérience
Travailler sur RésaResto a été une expérience particulièrement enrichissante. Ce projet m’a permis de mener une approche full-stack complète, en gérant aussi bien la conception du back-end, le développement du front-end, que le déploiement et l'automatisation via Kubernetes et ArgoCD.
J’ai pu approfondir mes compétences en C# .NET, Flutter, Docker, tout en développant une vision globale du cycle de vie d’un projet. L’un des défis majeurs a été l’orchestration des différents services et la mise en place d’un CI/CD efficace, garantissant un déploiement rapide et fiable sans interruption du service.
Voir l’application être utilisée en conditions réelles et répondre aux besoins pour lesquels elle a été conçue est une véritable source de satisfaction. Cette expérience m’a conforté dans l’idée que le développement ne se limite pas à l’écriture du code, mais implique aussi une réflexion stratégique sur l’architecture, la maintenance et l’évolutivité d’une solution.
Si je devais améliorer certains aspects du projet, je mettrais davantage l’accent sur l’expérience utilisateur, en intégrant un design plus intuitif et une gestion des notifications améliorée pour informer les utilisateurs des nouvelles propositions ou décisions prises au sein d’un groupe. Une optimisation de la gestion des groupes pourrait également renforcer la simplicité d’organisation.
En résumé, RésaResto a été une opportunité précieuse pour consolider mes compétences techniques, mais aussi pour apprendre à gérer un projet de bout en bout, en intégrant les bonnes pratiques DevOps, tout en garantissant une évolution continue de l’application.
Compétences liées
C# .NET - Flutter - Docker - Base de données - Maquettage - Autonomie - Gestion de projet
