Fermer

mars 25, 2024

Principes de base d’ASP.NET Core : organiser des projets – Modèles architecturaux

Principes de base d’ASP.NET Core : organiser des projets – Modèles architecturaux


L’organisation des projets dans ASP.NET Core est essentielle pour maintenir un code propre et gérable. Les modèles architecturaux peuvent aider. Consultez cet article de blog sur la mise en œuvre de l’un des modèles architecturaux les plus connus, le modèle en oignon.

Les modèles d’architecture sont des approches de conception qui aident à organiser et à structurer les applications Web pour une maintenabilité, une évolutivité et une flexibilité optimales.

Cet article explorera l’une des normes les plus largement adoptées aujourd’hui : l’architecture en oignon. De plus, nous discuterons de l’application pratique de ce modèle dans une application ASP.NET Core.

Que sont les modèles architecturaux ?

Les modèles d’architecture sont des solutions ou des modèles de conception de haut niveau qui fournissent une approche structurée pour organiser et concevoir l’architecture des systèmes logiciels.

Ces modèles offrent un ensemble de bonnes pratiques et de lignes directrices pour résoudre les problèmes de conception courants rencontrés par les développeurs lors du développement d’applications complexes. Les modèles architecturaux aident les systèmes logiciels à être évolutifs, faciles à entretenir et adaptables aux exigences changeantes.

Les principales caractéristiques des modèles architecturaux comprennent :

  1. Réutilisabilité : Les modèles architecturaux sont des solutions réutilisables qui peuvent être appliquées à différents projets et domaines. Ils regroupent l’expertise en conception, permettant aux développeurs d’appliquer plus facilement des concepts de conception éprouvés.

  2. Abstraction: Les modèles fournissent un niveau d’abstraction qui se concentre sur la structure et l’organisation de haut niveau d’un système plutôt que sur des détails spécifiques de mise en œuvre. Cette abstraction permet aux développeurs de réfléchir à l’architecture système d’une manière plus conceptuelle et générale.

  3. Évolutivité : Les modèles architecturaux sont conçus pour s’adapter à la croissance future et aux exigences changeantes. Ils contribuent à garantir qu’un système peut évoluer en termes de fonctionnalités et de performances.

  4. Entretien: En favorisant une séparation claire des préoccupations et la modularisation des composants, les modèles architecturaux facilitent la maintenance et l’extension d’un système logiciel dans le temps.

  5. Cohérence: Les modèles établissent une structure et une approche de conception cohérentes, ce qui peut être bénéfique dans les environnements d’équipe et les grands projets logiciels.

  6. Documentation: Les normes sont accompagnées d’une grande quantité de documentation et de ressources, ce qui aide les développeurs à les comprendre et à les appliquer efficacement.

Dans le contexte d’ASP.NET Core, il existe certains modèles largement utilisés, notamment :

  • Architecture de l’oignon
  • Architecture hexagonale (ports et adaptateurs)
  • Architecture épurée (évolution de l’architecture oignon)
  • Architecture sans serveur

Dans cet article, nous découvrirons l’un de ces modèles : le modèle d’architecture en oignon.

Quel est le modèle d’architecture de l’oignon ?

Le modèle d’architecture onion est un modèle d’architecture logicielle largement utilisé dans ASP.NET Core et d’autres frameworks de développement d’applications modernes. Il s’agit d’une variante de l’architecture en couches traditionnelle qui favorise une manière plus flexible et durable de concevoir et de structurer des applications. Jeffrey Palermo a popularisé le modèle d’architecture oignon, particulièrement adapté et recommandé pour créer des applications robustes, maintenables et testables.

L’idée principale de l’architecture oignon est d’organiser l’application en cercles ou couches concentriques, chaque couche dépendant uniquement des couches internes.

Le motif Oignon

Ensuite, découvrons et implémentons chacune des quatre couches principales d’une application d’architecture oignon typique dans ASP.NET Core.

Création du projet

Pour créer l’exemple de projet, vous devez disposer des éléments suivants :

  • .NET7 ou supérieur installé
  • IDE de votre choix (cet article utilisera Visual Studio Code)

Vous pouvez accéder au code source complet ici : Code source.

A la fin du poste, le projet complet aura la structure suivante :

Structure du projet

Tout d’abord, créons un projet de solution ASP.NET Core, dans lequel nous stockerons les couches d’application. Donc, dans le terminal, exécutez la commande suivante :

dotnet new sln -n BookingFast

Implémentation des couches

1. Couche de domaine

Il s’agit de la couche la plus interne et contient la partie la plus critique de la logique métier, représentant le cœur de l’application, et doit être totalement indépendante de toute structure externe. Dans la couche principale, vous définissez vos modèles de domaine, vos règles métier et votre logique spécifique à l’application. Cette couche ne doit avoir aucune dépendance par rapport aux autres couches et est souvent appelée couche « Domaine » ou « Entités ».

Pour créer la couche de domaine dans le projet et l’ajouter à la classe de solution, utilisez les commandes suivantes :

dotnet new classlib -n BookingFast.Domain
dotnet sln BookingFast.sln add BookingFast.Domain/BookingFast.Domain.csproj

Maintenant, dans le dossier « BookingFast.Domain », créez un nouveau dossier appelé « Entités » et à l’intérieur, créez la classe suivante :

namespace BookingFast.Domain.Entities;

public class Reservation
{
   public Reservation(Guid id, string guestName, DateTime checkInDate, DateTime checkOutDate, string status)
    {
        Id = id;
        GuestName = guestName;
        CheckInDate = checkInDate;
        CheckOutDate = checkOutDate;
        Status = status;
    }

    public Guid Id { get; set; }
    public string? GuestName { get; set; }
    public DateTime CheckInDate { get; set; }
    public DateTime CheckOutDate { get; set; }
    public string? Status { get; set; }
}

La classe « Réservation » est l’entité principale de notre application, elle appartient donc à la couche de domaine, qui est la couche la plus interne d’une structure en oignon.

Créons ensuite la couche Infrastructure.

2. Couche d’infrastructure

La couche infrastructure est responsable de l’interaction avec les systèmes, structures et services externes. Dans le contexte d’ASP.NET Core, cette couche inclut le code lié à l’accès aux données, à la communication avec les services externes et à d’autres problèmes d’infrastructure. Cette couche peut avoir des dépendances sur des bibliothèques externes, des frameworks et ASP.NET Core lui-même.

Pour créer la couche Infrastructure et l’ajouter à la solution, à la racine du projet exécutez les commandes suivantes :

dotnet new classlib -n BookingFast.Infrastructure
dotnet sln BookingFast.sln add BookingFast.Infrastructure/BookingFast.Infrastructure.csproj

Tout d’abord, nous devons télécharger les dépendances sur la couche infrastructure, ouvrez donc un terminal dans le projet d’infrastructure et exécutez les commandes suivantes :

dotnet add package Microsoft.Extensions.Options.ConfigurationExtensions --version 8.0.0
dotnet add package MongoDB.Driver --version 2.22.0

Créons maintenant la classe qui contiendra les variables chargées de stocker les valeurs de la chaîne de connexion à la base de données.

Ensuite, dans le dossier « BookingFast.Infrastructure », créez un nouveau dossier appelé « Référentiels ». À l’intérieur, créez une nouvelle classe appelée « StudentDatabaseSettings » et placez-y le code suivant :

namespace BookingFast.Infrastructure.Repositories;
public class ReservationsDatabaseSettings
{
    public string ConnectionString { get; set; }
    public string DatabaseName { get; set; }
    public string CollectionName { get; set; }
}

Dans cet exemple, nous allons créer une base de données dans MongoDB Atlas, qui est une base de données très simple. Pour ce faire, vous devez d’abord créer un serveur dans MongoDB Atlas et créer la base de données. Si vous êtes nouveau sur MongoDB Atlas, je vous recommande ce guide pour créer et configurer votre premier cluster : Atlas MongoDB – Mise en route (Onglet de l’interface utilisateur d’Atlas).

Une fois le cluster configuré, nous pouvons créer une base de données « reservation_db » et une collection « étudiants » comme dans l’image ci-dessous :

Créer une base de données

Pour connecter notre application au cluster et accéder à la base de données créée, nous devons obtenir la chaîne de connexion, que nous utiliserons plus tard. Pour l’obtenir, suivez simplement les étapes indiquées dans les images ci-dessous :

Dans votre base de données, cliquez sur « Connecter » > « Pilotes » et dans la fenêtre copiez la chaîne de connexion, comme indiqué dans l’image ci-dessous.

Obtenir la chaîne de connexion

Maintenant que nous avons la connexion au cluster, implémentons la configuration dans le projet. Remplacez le code dans le fichier « appsettings.json » de la couche « BookingFast.UI » par le code ci-dessous :

{
  "ReservationsDatabaseSettings": {
    "ConnectionString": "<your cluster connection>",
    "DatabaseName": "reservations_db",
    "CollectionName": "reservations",
    "IsSSL": true
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*"
}

Dans le code ci-dessus, remplacez "<your cluster connection>" avec votre connexion de cluster précédemment obtenue. Pensez également à remplacer "<password>" et
"<username>" avec le mot de passe du cluster et le nom d’utilisateur.

Créons maintenant l’interface du référentiel avec les méthodes responsables des opérations de base de données.

Dans une architecture oignon, une interface de référentiel se trouve généralement au niveau de la couche domaine, car les référentiels font partie de la logique d’accès aux données et constituent un élément fondamental du domaine d’application.

Ainsi, dans le dossier « BookingFast.Domain », créez un nouveau dossier appelé « Infra ». À l’intérieur de celui-ci, créez un autre dossier nommé « Interfaces » et ajoutez-y l’interface suivante :

using BookingFast.Domain.Entities;

namespace BookingFast.Domain.Infra.Interfaces;

public interface IReservationsRepository
{
    Task<IEnumerable<Reservation>> FindAllReservations();
    Task InsertReservation(Reservation reservation);
    Task UpdateReservationStatus(string status);
}

Pour créer la classe Repository, nous utiliserons la couche infrastructure, donc dans le dossier « BookingFast.Infrastructure », dans le dossier « Repositories », créez la classe ci-dessous :

Référentiel de réservations

using BookingFast.Domain.Entities;
using BookingFast.Domain.Infra.Interfaces;
using Microsoft.Extensions.Options;
using MongoDB.Driver;

namespace BookingFast.Infrastructure.Repositories;

public class ReservationsRepository : IReservationsRepository
{
    private readonly IMongoCollection<Reservation> _reservations;

    public ReservationsRepository(IOptions<ReservationsDatabaseSettings> options)
    {
        var mongoClient = new MongoClient(options.Value.ConnectionString);

        _reservations = mongoClient
            .GetDatabase(options.Value.DatabaseName)
            .GetCollection<Reservation>(options.Value.CollectionName);
    }

    public async Task<IEnumerable<Reservation>> FindAllReservations()
    {
        if (_reservations == null)
            return Enumerable.Empty<Reservation>();

        return await _reservations.Find(_ => true).ToListAsync();
    }

    public async Task InsertReservation(Reservation reservation)
    {
        await _reservations.InsertOneAsync(reservation);
    }

    public async Task UpdateReservationStatus(string status, Guid id)
    {
        var reservation = await _reservations.Find(a => a.Id == id).SingleOrDefaultAsync();
        reservation.Status = status;
        await _reservations.ReplaceOneAsync(a => a.Id == id, reservation);
    }
}

Notez que nous utilisons l’interface « IReservationsRepository », qui provient de la couche domaine. Pour l’utiliser, nous devons ajouter la référence à la couche domaine dans la couche infrastructure. Alors, double-cliquez sur le fichier « BookingFast.Infrastructure.csproj » et ajoutez-y le code ci-dessous :

  <ItemGroup>
    <ProjectReference Include="..\BookingFast.Domain\BookingFast.Domain.csproj" />
  </ItemGroup>

La couche Infrastructure est prête, la prochaine étape consiste à implémenter la couche Application où nous allons créer la classe de service.

3. Couche applicative

Le cercle concentrique suivant est la couche application, qui dépend de la couche domaine mais ne devrait pas non plus avoir de dépendances vis-à-vis de frameworks externes. Cette couche contient des services spécifiques à l’application, des cas d’utilisation et une logique d’application. Il agit comme intermédiaire entre la couche omain et les couches externes telles que les couches UI et infrastructure.

Pour créer la couche application et l’ajouter à la solution, exécutez les commandes suivantes dans le terminal, à la racine du projet :

dotnet new classlib -n BookingFast.Application
    dotnet sln BookingFast.sln add BookingFast.Application/BookingFast.Application.csproj

Tout d’abord, créons les classes d’objets de transfert de données (DTO) qui seront les classes exposées à la couche d’interface utilisateur et qui représentent le modèle d’entité. Dans ce cas, il s’agira de la classe « ReservationDto ».

Ainsi, dans le dossier « BookingFast.Application », créez un nouveau dossier appelé « Dtos » et à l’intérieur, créez une nouvelle classe appelée « ReservationDto ». Placez-y le code ci-dessous :

using BookingFast.Domain.Entities;

namespace BookingFast.Application.Dtos;

public class ReservationDto
{
    public ReservationDto() { }

    public ReservationDto(Reservation reservation)
    {
        Id = reservation.Id;
        GuestName = reservation.GuestName;
        CheckInDate = reservation.CheckInDate;
        CheckOutDate = reservation.CheckOutDate;
        Status = reservation.Status;
    }

    public Guid Id { get; set; }
    public string? GuestName { get; set; }
    public DateTime CheckInDate { get; set; }
    public DateTime CheckOutDate { get; set; }
    public string? Status { get; set; }
}

Ici, nous devons également ajouter les dépendances des autres couches, qui sont le domaine et l’infrastructure, alors double-cliquez sur le fichier « BookingFast.Application » et ajoutez le code ci-dessous :

 <ItemGroup>
    <ProjectReference Include="..\BookingFast.Domain\BookingFast.Domain.csproj" />
    <ProjectReference Include="..\BookingFast.Infrastructure\BookingFast.Infrastructure.csproj" />
  </ItemGroup>

L’étape suivante consiste à créer la classe de service et les méthodes permettant d’effectuer les opérations bancaires via la couche infrastructure. Dans le dossier « BookingFast.Application », créez un nouveau dossier appelé « Services » et à l’intérieur, créez l’interface et la classe suivantes :

using BookingFast.Application.Dtos;

namespace BookingFast.Application.Services;

public interface IReservationsService
{
    Task<List<ReservationDto>> FindAllReservations();
    Task CreateNewReservation(ReservationDto reservation);
    Task UpdateReservationStatus(string status, Guid id);
}
using BookingFast.Application.Dtos;
using BookingFast.Domain.Entities;
using BookingFast.Domain.Infra.Interfaces;

namespace BookingFast.Application.Services;

public class ReservationsService : IReservationsService
{
    private readonly IReservationsRepository _reservationsRepository;

    public ReservationsService(IReservationsRepository reservationsRepository)
    {
        _reservationsRepository = reservationsRepository;
    }

    public async Task<List<ReservationDto>> FindAllReservations()
    {
        var reservations = await _reservationsRepository.FindAllReservations();
        return reservations.Select(reservation => new ReservationDto(reservation)).ToList();
    }

    public async Task CreateNewReservation(ReservationDto reservation)
    {
        var newReservation = new Reservation(reservation.Id, reservation.GuestName, reservation.CheckInDate, reservation.CheckOutDate, reservation.Status);
        await _reservationsRepository.InsertReservation(newReservation);
    }

    public async Task UpdateReservationStatus(string status, Guid id)
    {
        await _reservationsRepository.UpdateReservationStatus(status, id);
    }
}

4. Couche d’interface utilisateur

Le cercle le plus extérieur est la couche UI, qui comprend les composants de l’interface utilisateur de l’application. Dans le contexte d’ASP.NET Core, cette couche comprend des contrôleurs, des vues et d’autres composants responsables de la gestion des requêtes HTTP, des entrées utilisateur et du rendu de l’interface utilisateur. La couche d’interface utilisateur dépend des couches d’application et d’infrastructure, mais ne doit contenir aucune logique métier. Il gère principalement les interactions des utilisateurs et invoque les services d’application.

Pour créer le projet d’interface utilisateur, exécutez la commande ci-dessous :

dotnet new web -n BookingFast.UI

Cette commande créera un nouveau projet à l’aide du modèle d’API ASP.NET Core Minimal. Ensuite, exécutez les commandes suivantes pour ajouter le projet « BookingFast.UI » à la classe de solution :

dotnet sln BookingFast.sln add BookingFast.UI/BookingFast.UI.csproj

Ajoutons maintenant la référence à la couche application. Double-cliquez sur le fichier « BookingFast.UI.csproj » et ajoutez l’extrait de code suivant :

<ItemGroup>
    <ProjectReference Include="..\BookingFast.Application\BookingFast.Application.csproj" />
  </ItemGroup>

Ensuite, téléchargeons les packages NuGet sur la couche UI. Ouvrez un terminal dans le projet UI et exécutez les commandes suivantes :

dotnet add package Microsoft.AspNetCore.OpenApi --version 8.0.0
dotnet add package Swashbuckle.AspNetCore --version 6.5.0

L’étape suivante consiste à créer le contrôleur, qui appellera les méthodes de la classe de service et exposera les données via les points de terminaison.

Dans la couche UI, créez un nouveau dossier appelé « Contrôleurs ». À l’intérieur, créez un nouveau fichier appelé « ReservationsController.cs » et placez-y le code ci-dessous :

using BookingFast.Application.Dtos;
using BookingFast.Application.Services;
using Microsoft.AspNetCore.Mvc;

namespace BookingFast.UI.Controllers;

[ApiController]
[Route("[controller]")]
public class ReservationsController : Controller
{
    private readonly IReservationsService _reservationsService;

    public ReservationsController(IReservationsService reservationsService)
    {
        _reservationsService = reservationsService;
    }

    [HttpGet]
    public async Task<ActionResult<List<ReservationDto>>> FindAllReservations()
    {
        var reservations = await _reservationsService.FindAllReservations();
        return Ok(reservations);
    }

    [HttpPost]
    public async Task<IActionResult> CreateNewReservation([FromBody] ReservationDto reservation)
    {
        await _reservationsService.CreateNewReservation(reservation);
        return Ok();
    }

    [HttpPut("{id}")]
    public async Task<IActionResult> UpdateReservationStatus(string status, Guid id)
    {
        await _reservationsService.UpdateReservationStatus(status, id);
        return Ok();
    }
}

La dernière étape consiste à configurer l’injection de dépendances des classes. Dans le fichier « Program.cs », remplacez le code existant par le code ci-dessous :

using BookingFast.Application.Services;
using BookingFast.Domain.Infra.Interfaces;
using BookingFast.Infrastructure.Repositories;

var builder = WebApplication.CreateBuilder(args);
builder.Services.Configure<ReservationsDatabaseSettings>(builder.Configuration.GetSection("ReservationsDatabaseSettings"));
builder.Services.AddSingleton<IReservationsRepository, ReservationsRepository>();
builder.Services.AddScoped<IReservationsService, ReservationsService>();

builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Tester l’application

Pour tester l’application, ouvrez un terminal dans le projet UI et exécutez la commande suivante :

dotnet run

Dans le navigateur, accédez à l’adresse http://localhost:5202/swagger/index.html et vous pouvez exécuter les opérations dans l’interface Swagger, comme indiqué dans le GIF ci-dessous :

Tester l'application

Conclusion

En résumé, le modèle architectural oignon se distingue comme une approche remarquable pour structurer et maintenir efficacement les projets ASP.NET Core. Tout au long de cet article, nous avons exploré les bases de ce modèle et examiné son application pratique.

Chaque fois que vous créez un nouveau projet, pensez à utiliser le motif en oignon. De cette façon, vous profiterez non seulement des avantages structurels offerts par le modèle, mais vous investirez également dans un code plus lisible, plus durable et plus facile à entretenir.




Source link