- Added DatabaseService to handle database operations for messages and support requests. - Created IDatabaseService interface to define the contract for database operations. - Developed ViewModels for Dashboard, Messages, and Support pages to manage data and commands. - Implemented XAML views for Dashboard, Messages, and Support, including data binding and UI elements. - Created SQL script for setting up the database schema and inserting test data.
8.9 KiB
Architecture de TravelMate Admin
🏗️ Pattern MVVM (Model-View-ViewModel)
Cette application utilise strictement le pattern MVVM avec CommunityToolkit.Mvvm pour une séparation claire des responsabilités.
┌─────────────────────────────────────────────────────────┐
│ VIEW │
│ (XAML + Code-behind) │
│ DashboardPage │ MessagesPage │ SupportPage │
└─────────────┬───────────────────────────────────────────┘
│ Data Binding
▼
┌─────────────────────────────────────────────────────────┐
│ VIEWMODEL │
│ (Business Logic + State) │
│ DashboardVM │ MessagesVM │ SupportVM │
└─────────────┬───────────────────────────────────────────┘
│ Calls
▼
┌─────────────────────────────────────────────────────────┐
│ SERVICE │
│ (Data Access Layer) │
│ DatabaseService │
└─────────────┬───────────────────────────────────────────┘
│ SQL Queries
▼
┌─────────────────────────────────────────────────────────┐
│ DATABASE │
│ MariaDB / MySQL │
│ messages │ support_requests │
└─────────────────────────────────────────────────────────┘
📂 Structure des Dossiers
/Models
Responsabilité : Représentation des données métier
Message.cs: Représente un message de demandeSupportRequest.cs: Représente une demande support
Caractéristiques :
- POCOs (Plain Old CLR Objects)
- Propriétés avec getters/setters
- Propriétés calculées (FullName, StatusText, etc.)
- Pas de logique métier
/Services
Responsabilité : Couche d'accès aux données
IDatabaseService.cs: Contrat du serviceDatabaseService.cs: Implémentation avec MySqlConnector
Caractéristiques :
- Méthodes asynchrones (Task)
- Gestion des connexions MySQL
- Gestion des erreurs avec try/catch
- Logs de débogage
Méthodes principales :
Task<List<T>> GetAllAsync()
Task<List<T>> GetFilteredAsync(bool? isDone)
Task<int> GetPendingCountAsync()
Task<int> GetDoneCountAsync()
Task<bool> UpdateStatusAsync(int id, bool done)
Task<bool> TestConnectionAsync()
/ViewModels
Responsabilité : Logique de présentation et état de l'UI
DashboardViewModel.cs: Gestion du dashboardMessagesViewModel.cs: Gestion de la liste des messagesSupportViewModel.cs: Gestion des demandes support
Caractéristiques :
- Hérite de
ObservableObject(CommunityToolkit.Mvvm) - Utilise
[ObservableProperty]pour les propriétés bindables - Utilise
[RelayCommand]pour les commandes - Injection de dépendance via constructeur
Exemple de propriété observable :
[ObservableProperty]
private bool isLoading;
// Génère automatiquement IsLoading avec INotifyPropertyChanged
Exemple de commande :
[RelayCommand]
public async Task LoadMessagesAsync()
{
// Logique de chargement
}
// Génère automatiquement LoadMessagesCommand : ICommand
/Views
Responsabilité : Interface utilisateur
DashboardPage.xaml/.csMessagesPage.xaml/.csSupportPage.xaml/.cs
Caractéristiques :
- XAML pour la structure UI
- Code-behind minimal (uniquement OnAppearing)
- Data Binding vers le ViewModel
- Converters pour la présentation
/Converters
Responsabilité : Conversion de données pour l'affichage
BoolConverters.cs:BoolToStatusTextConverter: bool → "Marquer comme fait"BoolToColorConverter: bool → Color
/Configuration
Responsabilité : Configuration de l'application
AppSettings.cs: Paramètres de connexion DB
🔄 Flux de Données
1. Chargement des Données
User Opens Page
↓
OnAppearing() appelé
↓
LoadCommand.Execute()
↓
ViewModel appelle Service
↓
Service exécute requête SQL
↓
Données retournées au ViewModel
↓
ViewModel met à jour ObservableCollection
↓
INotifyPropertyChanged déclenché
↓
UI se met à jour automatiquement
2. Action Utilisateur (Changer statut)
User clique sur Button
↓
Command bindée exécutée
↓
ViewModel.ToggleStatusCommand
↓
Service.UpdateStatusAsync(id, !done)
↓
SQL UPDATE exécuté
↓
Success → ViewModel met à jour l'objet local
↓
INotifyPropertyChanged → UI refresh
🧩 Injection de Dépendances
Configuration dans MauiProgram.cs :
// Services (Singleton car un seul pour toute l'app)
builder.Services.AddSingleton<IDatabaseService, DatabaseService>();
// ViewModels
builder.Services.AddSingleton<DashboardViewModel>(); // Singleton = même instance
builder.Services.AddTransient<MessagesViewModel>(); // Transient = nouvelle instance
// Views
builder.Services.AddSingleton<DashboardPage>();
builder.Services.AddTransient<MessagesPage>();
Pourquoi Singleton vs Transient ?
- Singleton : Dashboard (état partagé, chargé une fois)
- Transient : Pages de liste (rafraîchies à chaque visite)
🎨 Navigation
Gestion via AppShell.xaml :
<TabBar>
<ShellContent Title="Dashboard" Route="DashboardPage" ... />
<ShellContent Title="Messages" Route="MessagesPage" ... />
<ShellContent Title="Support" Route="SupportPage" ... />
</TabBar>
Navigation programmatique :
await Shell.Current.GoToAsync("//MessagesPage");
🔐 Sécurité
Actuellement :
- Mot de passe en clair dans AppSettings.cs
Pour la Production :
- Utiliser variables d'environnement
- Chiffrer la chaîne de connexion
- Utiliser Azure Key Vault ou équivalent
- Implémenter authentification utilisateur
- Logger les actions sensibles
📊 Performance
Optimisations actuelles :
- Connexions fermées automatiquement (using)
- Requêtes asynchrones (async/await)
- Filtres côté base de données (WHERE clause)
- Tri côté base de données (ORDER BY)
Optimisations futures possibles :
- Pagination pour grandes listes
- Mise en cache des données
- Connection pooling (déjà géré par MySqlConnector)
- Lazy loading
🧪 Testabilité
Architecture testable grâce à :
- Interface
IDatabaseService(mockable) - ViewModels découplés des Views
- Injection de dépendances
- Logique métier dans ViewModels
Exemple de test unitaire (potentiel) :
[Test]
public async Task LoadMessages_ShouldPopulateCollection()
{
// Arrange
var mockService = new Mock<IDatabaseService>();
mockService.Setup(s => s.GetAllMessagesAsync())
.ReturnsAsync(new List<Message> { /* ... */ });
var vm = new MessagesViewModel(mockService.Object);
// Act
await vm.LoadMessagesCommand.ExecuteAsync(null);
// Assert
Assert.That(vm.Messages.Count, Is.GreaterThan(0));
}
🚀 Extensibilité
Pour ajouter une nouvelle entité :
- Model : Créer
NewEntity.cs - Service : Ajouter méthodes dans
IDatabaseServiceetDatabaseService - ViewModel : Créer
NewEntityViewModel.cs - View : Créer
NewEntityPage.xaml/.cs - DI : Enregistrer dans
MauiProgram.cs - Navigation : Ajouter dans
AppShell.xaml
📝 Conventions de Code
-
Naming :
- ViewModels :
*ViewModel.cs - Pages :
*Page.xaml/.cs - Services :
*Service.csavec interfaceI*Service.cs
- ViewModels :
-
Async :
- Toujours utiliser
async/await - Suffixe
Asyncpour les méthodes
- Toujours utiliser
-
MVVM :
- Pas de logique dans code-behind (sauf OnAppearing)
- Tout passe par le ViewModel
- Binding bidirectionnel quand nécessaire
Cette architecture garantit :
- ✅ Maintenabilité
- ✅ Testabilité
- ✅ Séparation des responsabilités
- ✅ Réutilisabilité
- ✅ Extensibilité