Bienvenue dans la cinquième partie de notre série de tutoriels sur l’intégration d’Apache Kafka dans une application moderne ! Nous avons déjà parcouru un chemin significatif. En effet, après avoir posé les bases architecturales dans la première partie, puis mis en place notre environnement de développement avec Docker Compose dans la deuxième partie, et enfin construit et enrichi notre backend Spring Boot avec Kafka dans les troisième et quatrième parties, il est temps d’aborder l’aspect visuel de notre application : son interface utilisateur.
Maintenant que notre backend Spring Boot est opérationnel et prêt à interagir avec MySQL et Kafka, nous allons donc concevoir une interface simple pour interagir avec son API. Autrement dit, c’est le moment de voir concrètement les fruits de notre travail prendre forme sous vos yeux !
🎯 L’objectif ici n’est pas de créer une application frontend complète avec toutes les fioritures d’une application de messagerie sociale complexe. Au lieu de cela, il s’agit uniquement d’une interface de test minimaliste, conçue pour :
- Envoyer de nouveaux messages via l’API REST de notre backend.
- Afficher les messages déjà stockés dans MySQL et récupérés via l’API.
- Vérifier ainsi que le flux complet du message (de la saisie à la persistance, en passant par Kafka, jusqu’à l’affichage) fonctionne correctement de bout en bout.
Nous avons choisi Vue.js pour cette interface en raison de sa simplicité et de sa courbe d’apprentissage rapide. Cependant, gardez à l’esprit que notre backend expose une API REST classique. Par conséquent, n’importe quel autre client HTTP, qu’il s’agisse de React, Angular, ou même une simple application mobile native, serait parfaitement compatible.
💡 Code source complet de l’exercice
Vous pouvez consulter et cloner le code source correspondant à ce tutoriel sur GitLab :
👉gitlab.com/springboot-kafka-social-lab/tag=v1.0.0
Ce tag v1.0.0 correspond à la version réalisée dans ce tutoriel.
De plus, le projet continuera à évoluer. Par conséquent, vous pouvez également consulter la branche main ou develop pour suivre les mises à jour ultérieures.
1. Création de l’interface Messages.vue
Pour cette mini-application de messagerie, nous avons opté pour un composant unique en Vue.js. Ce composant aura la double responsabilité d’afficher les messages existants et de permettre d’en poster de nouveaux. Cela permet de maintenir l’interface simple et de se concentrer sur l’essentiel : le flux de données à travers notre architecture.
1.1. Le code du composant Messages.vue
Voici le code complet de notre composant Vue.js. Il encapsule la logique d’affichage et d’interaction avec l’API :
<template>
<div class="messages-app">
<h2>Social Messages</h2>
<form @submit.prevent="postMessage">
<input v-model="newMessage" placeholder="Type your message..." required />
<button type="submit" :disabled="loading">Send</button>
</form>
<ul>
<li v-for="msg in messages" :key="msg.id">
<strong>#{{ msg.id }}</strong> {{ msg.content }}
<span class="date">{{ formatDate(msg.createdAt) }}</span>
</li>
</ul>
</div>
</template>
<script lang="ts" setup>
import { ref, onMounted } from 'vue'
import axios from 'axios'
interface Message {
id: number
content: string
createdAt: string
}
const API_BASE = import.meta.env.VITE_API_BASE || 'http://localhost:8080/api/messages'
const messages = ref<Message[]>([])
const newMessage = ref('')
const loading = ref(false)
function loadMessages() {
axios.get<Message[]>(API_BASE)
.then(res => messages.value = res.data)
.catch(() => alert('Failed to load messages'))
}
function postMessage() {
if (!newMessage.value.trim()) return
loading.value = true
axios.post<Message>(API_BASE, { content: newMessage.value })
.then(() => {
newMessage.value = ''
loadMessages()
})
.catch(() => alert('Failed to post message'))
.finally(() => loading.value = false)
}
function formatDate(dateStr: string) {
return new Date(dateStr).toLocaleString()
}
onMounted(() => {
loadMessages()
})
</script>
<style scoped>
.messages-app { max-width: 500px; margin: auto; }
form { margin-bottom: 1em; display: flex; gap: 0.5em; }
input { flex: 1; padding: 0.5em; }
button { padding: 0.5em 1em; }
ul { list-style: none; padding: 0; }
li { padding: 0.5em 0; border-bottom: 1px solid #eee; }
.date { color: #aaa; font-size: 0.9em; margin-left: 1em; }
</style>
1.2. Décryptage du composant Vue.js
- Le formulaire, tout d’abord, permet d’envoyer un nouveau message avec axios.post vers notre API backend.
- La méthode loadMessages(), quant à elle, récupère les messages existants via axios.get depuis la même API.
- Par ailleurs, l’affichage est réactif grâce aux fonctionnalités de Vue 3, notamment l’utilisation de ref pour les données réactives, v-model pour la liaison bidirectionnelle du formulaire, et onMounted pour charger les messages dès le démarrage du composant.
- Enfin, l’URL de l’API est configurable via import.meta.env.VITE_API_BASE. Par défaut, elle pointe sur http://localhost:8080/api/messages, ce qui est idéal pour le développement local.
1.3. Validation du fonctionnement du Frontend
Une fois cette page ajoutée à votre projet Vue.js et l’application lancée, vous pourrez :
- Saisir un message dans le champ texte prévu à cet effet.
- L’envoyer au backend via l’API Spring Boot en cliquant sur le bouton « Send ».
- Voir le message s’afficher instantanément dans la liste avec son ID et sa date de création, confirmant ainsi le flux complet de données.
En somme, c’est une manière simple mais très efficace de vérifier le bon fonctionnement de l’écosystème complet : de l’interface frontend à l’API Spring Boot, en passant par la persistance MySQL et la publication/consommation Kafka (dont vous pourrez vérifier les logs côté backend).
2. Dockerisation du frontend Vue.js
Après avoir vérifié que l’interface Vue.js fonctionne correctement en local, il est désormais temps de la dockeriser. Cette étape est essentielle pour l’intégrer harmonieusement à l’ensemble de notre architecture multi-services, qui est orchestrée avec Docker Compose. En effet, la dockerisation garantit une portabilité et une reproductibilité accrues de notre application, quel que soit l’environnement.
2.1. Pourquoi dockeriser le frontend ?
Grâce à cette dockerisation, vous pourrez notamment :
- Simplifier considérablement les tests et les déploiements sur n’importe quel environnement, qu’il s’agisse de développement, de staging ou de production.
- Éviter d’installer Node.js et toutes ses dépendances directement sur votre machine locale, ce qui contribue à alléger et à nettoyer votre environnement de développement.
- Uniformiser les processus de build et de déploiement pour l’ensemble des développeurs au sein de l’équipe, assurant ainsi une cohérence.
- Intégrer facilement le frontend à des pipelines de CI/CD (comme GitLab CI/CD, que nous aborderons en détail dans la prochaine et dernière partie) ou à des environnements cloud.
2.2. Dockerfile du frontend : la construction de l’image
Pour dockeriser notre application Vue.js, nous allons créer un Dockerfile spécifique. Ce fichier décrira précisément toutes les étapes nécessaires à la construction d’une image Docker légère et prête à servir notre interface web de manière autonome.
Voici le contenu du fichier frontend/Dockerfile :
# --------- Build stage ---------
FROM node:20 AS build-stage
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
# --------- Production stage ---------
FROM nginx:stable-alpine AS production-stage
COPY --from=build-stage /app/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
2.2.1. Explication détaillée des étapes du Dockerfile
Ce Dockerfile est judicieusement organisé en deux phases distinctes, exploitant ainsi une approche appelée multi-stage build. Il est important de noter que cette méthode est extrêmement efficace pour optimiser la taille finale du conteneur, car elle ne conserve que les artefacts strictement nécessaires à l’exécution de l’application.
- Build stage (étape de compilation) :
- Premièrement, elle utilise une image officielle node:20 comme environnement de base pour la compilation de l’application Vue.js.
- Ensuite, elle définit le répertoire de travail à /app à l’intérieur du conteneur.
- Par la suite, elle copie les fichiers package.json et package-lock.json (ou yarn.lock) puis exécute npm install pour installer proprement toutes les dépendances. Grâce à cela, les dépendances sont mises en cache, ce qui accélère considérablement les builds futurs.
- Après cela, elle copie l’ensemble du code source du frontend dans le conteneur.
- Enfin, elle lance npm run build pour compiler l’application Vue.js et générer les fichiers statiques de production dans le dossier dist/.
- Production stage (étape de déploiement) :
- Pour commencer, elle utilise une image Nginx très légère (nginx:stable-alpine). Nginx, en effet, est un serveur web performant, particulièrement adapté pour servir des fichiers statiques.
- Ensuite, elle copie *uniquement* les fichiers compilés (c’est-à-dire le contenu du dossier dist/) depuis l’étape de build (build-stage) vers le répertoire public de Nginx (/usr/share/nginx/html). C’est précisément cette étape qui est garante de la petite taille de l’image finale.
- Par ailleurs, elle expose le port 80, qui est le port par défaut de Nginx, afin de rendre l’application web accessible depuis l’extérieur du conteneur.
- Enfin, elle définit la commande par défaut (CMD) pour démarrer Nginx en mode « daemon off ». Ce mode est crucial car il maintient le processus Nginx actif au premier plan dans le conteneur, permettant ainsi à Docker de le superviser.
- Build stage (étape de compilation) :
2.3. Intégration du frontend dans docker-compose.yml
Pour que Docker Compose puisse gérer et lancer notre frontend dockerisé aux côtés de notre backend et de nos services de base, il est impératif d’ajouter un nouveau service au fichier docker-compose.yml principal. Ce fichier, rappelons-le, est situé à la racine de votre projet.
Ajoutez ou vérifiez le bloc suivant dans votre fichier docker-compose.yml :
frontend:
build: ./frontend # Build using Dockerfile in /frontend
depends_on:
- backend # Optionnel : attendre que le backend soit prêt
ports:
- "80:80" # Application accessible sur http://localhost
Cette configuration suppose que votre frontend est situé dans le dossier
frontend/à la racine du projet, au même niveau que le dossier backend/. Il est important de respecter cette structure pour que le build fonctionne correctement.
2.4. Lancer l’environnement complet avec le frontend Dockerisé
Une fois tous les services démarrés avec la commande que nous avons vue précédemment (et que nous allons rappeler pour plus de commodité) :
docker compose up --build -d
Votre interface utilisateur Vue.js sera alors disponible et accessible via votre navigateur à l’adresse suivante :
Il est important de noter qu’elle communiquera directement avec l’API backend via une URL comme http://backend:8080/api/messages. Cette communication fluide est rendue possible grâce à la configuration de Vite (ou Vue CLI, selon votre configuration initiale du projet Vue.js) qui permet de définir une variable d’environnement VITE_API_BASE ou un proxy pour les requêtes API. De surcroît, le fait que les conteneurs Docker peuvent se résoudre par leurs noms de service (backend dans ce cas précis) facilite grandement cet échange.
Conclusion de l’article 5
Félicitations ! Vous avez non seulement créé une interface utilisateur simple et fonctionnelle avec Vue.js pour notre application de messagerie, mais vous avez également réussi l’étape cruciale de sa dockerisation et de son intégration dans notre environnement Docker Compose. Votre application fullstack est désormais entièrement assemblée et prête à être testée de bout en bout, de l’interface utilisateur jusqu’à la base de données et les topics Kafka. C’est une réalisation majeure !
La suite dans la Partie 6
Dans le prochain et dernier article de cette série, nous allons franchir une étape cruciale pour tout projet moderne : l’automatisation. Plus précisément, nous mettrons en place l’Intégration Continue et le Déploiement Continu (CI/CD) avec GitLab CI/CD. L’objectif sera d’automatiser les builds, les tests et le packaging Docker de notre application complète, garantissant ainsi un workflow de développement optimisé et des déploiements plus sereins. Préparez-vous à entrer dans l’ère de l’automatisation !
Pour rappel : la Partie 1 a posé les bases architecturales et présenté les prérequis essentiels. La Partie 2 a couvert la mise en place de l’environnement Dockerisé. La Partie 3 a détaillé la structure de base du backend Spring Boot. Enfin, la Partie 4 a enrichi notre backend avec l’intégration de Kafka et sa logique métier.
💬 Une question ou une remarque ?
Nous vous encourageons à poser vos questions en commentaire. Toute remarque ou suggestion est également la bienvenue !


