Dot.Blog

Consulting DotNet C#, XAML, WinUI, WPF, MAUI, IA

Construire une architecture modulaire en .NET sans microservices

Les microservices sont à la mode, mais ils ne sont pas toujours nécessaires. Pour des applications de bureau, embarquées, ou même certaines applications serveur, une architecture modulaire monolithique est souvent plus simple, performante et maintenable.

Dans cet article, nous allons explorer les techniques pour créer une architecture modulaire en .NET 9, avec séparation des responsabilités, extensibilité et découplage, tout en évitant la complexité des microservices.

🧩 Objectif : modularité sans dispersion

Créer une application qui :

  • Est déployée en un seul bloc (monolithe)
  • Contient plusieurs modules fonctionnels clairement séparés
  • Peut charger dynamiquement des composants
  • Offre des points d’extension sans surcharge technique

🧱 Organisation par module fonctionnel

Chaque module a sa propre bibliothèque (.csproj) :

  • App.Core
  • App.Billing
  • App.Reporting
  • App.Plugins

Chaque projet contient ses propres services, modèles et logique.

Exemple de structure :

/MonApp
├── MonApp.UI (WinUI ou WPF)
├── MonApp.Core
├── MonApp.Accounting
├── MonApp.Reporting
└── MonApp.PluginHost

🔌 Plugin system avec MEF ou Assembly scanning

Pour charger dynamiquement des modules :

var assemblies = Directory.GetFiles("./modules", "*.dll")
    .Select(Assembly.LoadFrom);

foreach (var asm in assemblies)
{
    var types = asm.GetTypes().Where(t => typeof(IModule).IsAssignableFrom(t));
    foreach (var type in types)
    {
        var module = (IModule)Activator.CreateInstance(type)!;
        module.RegisterServices(serviceCollection);
    }
}

Définir une interface commune :

public interface IModule
{
    void RegisterServices(IServiceCollection services);
}

🔁 Injection de dépendances entre modules

Utiliser une interface ou un événement pour découpler les appels :

public interface INotificationService
{
    void Notify(string message);
}
// Module Reporting dépend uniquement de l’interface

Permet d’injecter un comportement différent selon l’hôte (console, UI, test).

🧪 Cas d’usage : WinUI avec modules métier

  • L’app WinUI référence uniquement App.Core
  • Chaque vue charge dynamiquement ses composants
  • Les ViewModels restent légers et découplés

⚠️ À éviter

  • Le couplage fort entre projets (références croisées)
  • L’utilisation d’un conteneur de services global "magique"
  • Le partage d’états ou de modèles métier complexes sans contrat formel

✅ Bonnes pratiques

  • Créer un contrat d’interface pour chaque service partagé
  • Favoriser les événements ou médiateurs (IMediator) pour l’interaction entre modules
  • Documenter les points d’extension clairement
  • Utiliser des AssemblyLoadContext pour l’isolation si nécessaire

🔚 Conclusion

Une architecture modulaire bien pensée vous permet de faire évoluer votre application en toute sécurité, sans la dette organisationnelle d’un système distribué.

Trop souvent des ayatollah du code, souvent des consultants de type 100% Agile ou autre mode, viennent mettre leur nez dans un code simple pour le rendre inmaintenable, juste pour la beauté du geste, par endoctrinement dangereux parfois aussi, et sous le regard médusé de dirigeants hypnotisés par un discours bien huilé plein de termes à la mode. Je l'ai vu tant de fois, et ça marche. Hélas. Il existe pourtant toujours des façons simples de faire les choses, je dirais même que l'Art de l'informaticien n'est pas de balancer des recettes savantes (ou en ayant l'apparence) mais bien de délivrer ce qui est juste nécessaire à une App donnée en augmentant sa maintenabilité, en augmentant sa fiabilité et en diminuant les coûts de maintenance et la dette technique. Tout le reste est malheureusement un marché de dupe. Ne vous laissez-pas, et ne laissez pas vos dirigeants se faire manipuler par les marchands de rêves bien ficelés qui, au final, couteront une fortune à refaire plus tard...

Avec les capacités de .NET 9, vous pouvez charger dynamiquement des modules, structurer proprement vos dépendances, et construire une application robuste, extensible, et performante. Mais simplement et sans utiliser une terminologie alambiquée teintée de magie et souvent d'auto-suffisance hautaine.

Stay Tuned !

Faites des heureux, PARTAGEZ l'article !