Architecture d'une solution
Une solution Lagoon suit une organisation en couches. Chaque couche a un rôle identifié, ce qui rend le câblage applicatif prévisible entre l'UI Blazor WebAssembly, le serveur ASP.NET Core, le modèle EF Core et les traitements batch.
Vue d'ensemble des projets
La structure type sépare les projets suivants :
| Projet | Rôle |
|---|---|
APP.Client | UI Blazor WebAssembly : pages Razor, composants client et appels HTTP client. |
APP.Server | Backend API, hôte de l'application Blazor WebAssembly, authentification et middleware ASP.NET Core. |
APP.Model | ApplicationDbContext, entités EF Core, migrations et seeders. |
APP.Shared | Données partagées : ViewModels, constantes, énumérations, rôles et policies. |
APP.Services | Logique métier et enregistrement DI via Registry.cs. |
APP.Batch | Application console pour les traitements batch, jobs et commandes. |
Les dépendances suivent le découpage applicatif : APP.Shared alimente le client et le modèle ; APP.Model et APP.Services alimentent le serveur et le batch.
Points d'entrée
Les applications Lagoon exposent des points d'entrée courts. La classe Program délègue le démarrage au framework, et la classe Main décrit le comportement de l'application.
Serveur
Le serveur appelle LagoonWebApplication.Run<Main>(args). Le patron général est Main : LgAuthApplication<TDbContext, TUser> ; dans l'application de démonstration, Main hérite de LgAuthApplication<ApplicationDbContext, ApplicationUser>.
namespace Demo.Server;
/// <summary>
/// The entry point of the application.
/// </summary>
public class Program
{
/// <summary>
/// Application startup.
/// </summary>
/// <param name="args">The arguments.</param>
public static void Main(string[] args)
{
// Délègue la création et le démarrage du serveur ASP.NET Core à Lagoon.
LagoonWebApplication.Run<Main>(args);
}
}
using Lagoon.Server.Application;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Demo.Model.Models;
using Demo.Model.Seeds;
using Lagoon.Server.Application.IdentitySources;
using Demo.Server.Technical;
namespace Demo.Server;
/// <summary>
/// The web server application's configuration and event manager.
/// </summary>
public class Main : LgAuthApplication<ApplicationDbContext, ApplicationUser>
{
protected override void OnConfigureDbContext(DbContextOptionsBuilder builder)
{
// Configure le provider EF Core et les options du DbContext applicatif.
ApplicationDbContext.Configure(builder, Configuration, ApplicationInformation.IsDevelopment);
}
protected override void OnConfigureServices(IServiceCollection services)
{
// Enregistre les services métier de l'application.
Services.Registry.RegisterServices(services);
services.AddNotification<NotificationManager>();
}
protected override async Task OnStartAsync(IServiceProvider scopedServiceProvider, CancellationToken cancellationToken)
{
if (Configuration.GetValue("AutoUpdateDatabase", true))
{
// Applique les migrations puis appelle OnUpdateDatabaseAsync.
await UpdateDatabaseAsync(scopedServiceProvider, cancellationToken);
}
}
protected override async Task OnUpdateDatabaseAsync(IServiceProvider scopedServiceProvider, ApplicationDbContext db, CancellationToken cancellationToken)
{
// Initialise les données de référence.
UserManager<ApplicationUser> userManager = scopedServiceProvider.GetRequiredService<UserManager<ApplicationUser>>();
await DataSeeder.SeedAsync(db, userManager, cancellationToken);
if (ApplicationInformation.IsDevelopment)
{
// Ajoute des données de démonstration en développement.
await FakeDataSeeder.SeedAsync(db, cancellationToken);
}
}
protected override void OnConfigure(IApplicationBuilder app)
{
if (ApplicationInformation.IsDevelopment)
{
// Active le débogage Blazor WebAssembly en développement.
app.UseWebAssemblyDebugging();
}
app.UseBlazorFrameworkFiles();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
endpoints.MapControllers();
endpoints.MapLagoonFallback();
});
}
protected override void OnConfigureAuthentification(AuthenticationOptions options)
{
// Autorise l'accès non authentifié à la page index.html.
options.AllowAnonymousIndex = true;
// Active l'authentification par formulaire.
options.AllowFormAuthentication = true;
}
protected override void OnConfigureAuthorization(AuthorizationOptions options)
{
// Enregistre les policies partagées de l'application.
Policies.ConfigureAuthorization(options);
}
}
Les overrides courants sont OnConfigureServices, OnConfigureDbContext, OnStartAsync, OnUpdateDatabaseAsync, OnConfigure, OnConfigureAuthentification et OnConfigureAuthorization.
Client
Le client Blazor WebAssembly appelle LagoonWebAssemblyApplication.RunAsync<Main>(args). La classe Main hérite de LgCustomApplication.
namespace Demo.Client;
/// <summary>
/// The entry point of the application.
/// </summary>
public class Program
{
/// <summary>
/// Application startup.
/// </summary>
/// <param name="args">The arguments.</param>
public static Task Main(string[] args)
{
// Démarre l'application Blazor WebAssembly Lagoon.
return LagoonWebAssemblyApplication.RunAsync<Main>(args);
}
}
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
namespace Demo.Client;
/// <summary>
/// The web assembly application's configuration and event manager.
/// </summary>
public class Main : LgCustomApplication
{
protected override void OnConfigureBehavior(ApplicationBehavior app)
{
#if RELEASE
// Configure l'acceptation des CGU en build Release.
app.RequireEulaConsent = true;
app.EulaFromServer = true;
#endif
app.DevelopedByLogoUri = new List<string> { LOGO_URI_DZ };
app.GridView(options =>
{
// Active les filtres de résumé sur les GridView.
options.ShowSummaryFilters = true;
});
}
protected override void OnInitialize(WebAssemblyHostBuilder builder)
{
// Déclare le composant racine dans l'élément DOM cible.
builder.RootComponents.Add<Shared.App>("#app");
// Enregistre les services client de l'application.
Services.Registry.RegisterServices(builder.Services);
}
protected override void OnConfigureAuthorization(AuthorizationOptions options)
{
// Reprend les policies partagées avec le serveur.
Policies.ConfigureAuthorization(options);
}
}
Les overrides courants sont OnConfigureBehavior, OnInitialize et OnConfigureAuthorization.
Batch
Le batch appelle LagoonConsoleApplication.Run<Main>(args). La classe Main hérite de LgApplication<ApplicationDbContext> et les commandes sont déclarées avec des attributs comme [Subcommand] ou [Command].
namespace Demo.Batch;
/// <summary>
/// The entry point of the application.
/// </summary>
internal class Program
{
/// <summary>
/// Application startup.
/// </summary>
/// <param name="args">The arguments.</param>
public static int Main(string[] args)
{
// Démarre l'application console Lagoon et retourne le code de sortie.
return LagoonConsoleApplication.Run<Main>(args);
}
}
using Lagoon.Console.Application;
using Microsoft.EntityFrameworkCore;
namespace Demo.Batch;
/// <summary>
/// The console application's configuration and event manager.
/// </summary>
[Subcommand(typeof(UpdateDbCommand))]
public class Main : LgApplication<ApplicationDbContext>
{
public Main() : base() { }
protected override void OnConfigureDbContext(DbContextOptionsBuilder builder)
{
// Réutilise la configuration EF Core de l'application.
ApplicationDbContext.Configure(builder, Configuration, ApplicationInformation.IsDevelopment);
}
protected override void OnConfigureServices(IServiceCollection services)
{
// Enregistre les services disponibles pour les commandes batch.
Services.Registry.RegisterServices(services);
}
protected override Task OnShowStatusAsync(ShowStatusEventArgs e)
{
// Point d'extension pour ajouter un statut applicatif personnalisé.
return Task.CompletedTask;
}
}
Dans les commandes batch, les services sont injectés via les paramètres de méthode de OnExecute ou OnExecuteAsync.
Pipeline de build des modules
Les fichiers Modules/LagoonPreBuild.targets et Modules/LagoonPreBuild.UI.targets consomment deux propriétés MSBuild définies dans les projets de modules :
| Propriété | Rôle |
|---|---|
<LagoonPriority> | Définit l'ordre de chargement au runtime : 0 pour Core, 2 pour UI ou Server, 4 par défaut. |
<HasRootableComponents> | Vaut True quand le module expose des composants Blazor rootables. |
Le dossier Modules peut aussi basculer en mode packaging NuGet avec _IsPacking=true, configuré dans Directory.Build.props.
Génération des ressources (Lagoon.Build)
Les ressources des modules et des applications sont générées par les tâches MSBuild du package Lagoon.Build, exécutées automatiquement avant chaque build. Aucun outil externe n'est nécessaire : l'outil global lgn n'intervient plus dans le build (il reste utile pour ses autres commandes, comme lgn update).
Pour une application, ces tâches :
- agrègent les sources SCSS de
Lagoon.UI, des autres modulesLagoon.UI*référencés et du projet client, puis les compilent en CSS ; - regroupent les scripts JavaScript et les icônes ;
- fusionnent les dictionnaires
Dico.xml.
Les fichiers produits sont écrits dans le dossier Client/wwwroot/_vroot de l'application : c'est une sortie de build, à ne pas modifier ni versionner. Voir Thèmes et styles pour la personnalisation des styles.
En Release, le JavaScript est minifié avec Terser, ce qui nécessite node.js et npm sur le poste de build : le package Terser est installé automatiquement au premier usage dans ~/.lagoon/_prebuild (ou dans le dossier désigné par la variable d'environnement LAGOON_PREBUILD_CACHE). En Debug, les scripts sont seulement concaténés et rien n'est requis.
Pour aller plus loin
Consultez aussi Structure des projets, Premier projet et Mise à jour de la base de données.