Vous souhaitez mettre en place une architecture propre et maintenable sur votre APIUne API est un programme permettant à deux applications distinctes de communiquer entre elles et d’échanger des données. .NET Core ? Vous êtes au bon endroit !
À la fin de cet article, vous aurez une API RESTREST (REpresentational State Transfer) est un style d'architecture logicielle qui fonctionne sous un certain nombre de contraintes. avec une architecture clean !
C’est parti ! 🎉
Commençons par structurer notre projet.
Nous allons initialiser notre projet avec l’API Web ASP.NET.NET est le principal framework de l'univers Microsoft. Core, pour commencer avec un exemple de contrôleur.
Vous nommez votre projet comme vous le souhaitez :
J’ai choisi le .NET 6 la dernière version LTS au moment de créer cet article.
(NB1 : la différence principale avec les versions précédentes est le regroupement des fichiers Startup.cs et Program.cs en un seul fichier afin de simplifier le lancement d’une application)
(NB2 : Si vous choisissez une version inférieure à .NET6, il y aura certaines différences au niveau des injections de dépendances, je spécifierai pour chaque version.)
À partir de ce point, nous avons notre projet prêt à être lancé. Nous pouvons commencer la mise en place de la structure !
Nous allons créer 4 (ou plus si besoin) nouveaux projets Bibliothèque de classe :
Nous aurons donc l’architecture suivante :
Commençons par le projet Domain :
Dans celui-ci, nous allons créer plusieurs dossiers :
Base : Contient l’entité de base, BaseEntity
Models : Contient tous nos modèles qui dérive de BaseEntity
DTO : Contient tous nos DTO
Les suivants sont facultatifs :
Request : Contient tous nos records qui vont servir dans les requêtes
Query : Contient des objets qui permettront de mapper directement des résultats de requêtes SQLLangage permettant de communiquer avec une base de données. dans ceux-ci
NB : Les dossiers Request et Query ne sont pas obligatoires. C’est seulement dans le cas d’une grosse application, on préfère les séparer des DTO pour plus de lisibilité.
Dans le projet Infrastructure :
NB : Le dossier Migrations d’EntityFramework sera créé automatiquement lors de notre première migration
Dans le projet Applications :
On obtient le schéma de solution ci-dessous, qui nous permet de séparer chaque couche de notre application :
Passons à l’implémentation de nos fichiers de base !
On va commencer par créer notre entité par défaut :
BaseEntity.cs → Projet Domain, dossier Base
public abstract class BaseEntity
{
public int Id { get; set; }
}
NB: Pour une application déjà existante, on peut se passer du BaseEntity et utiliser “class” à la place dans les templates ci-dessous.
Grâce à celle-ci, nous allons pouvoir implémenter nos interfaces de base pour les repositories et les services :
IBaseRepository.cs → Projet Infrastructure, dossier Base
public interface IBaseRepository<T> where T : BaseEntity
{
Task<T> GetById(int id);
Task<List<T>> GetAllAsync();
Task<List<T>> GetAllAsync(Expression<Func<T, bool>> where);
}
(On pourra rajouter d’autres méthodes génériques par la suite, comme par exemple la sauvegarde, la mise à jour, la suppression, la récupération via une requête SQL native, etc.)
IBaseService.cs → Projet ApplicationC'est un programme conçu pour effectuer une ou plusieurs tâches. Réaliser des applications, c'est notre cœur de métier chez AXOPEN !, dossier Base
public interface IBaseService<T> where T : BaseEntity
{
Task<T> GetById(int id);
Task<List<T>> GetAllAsync();
Task<List<T>> GetAllAsync(Expression<Func<T, bool>> where);
}
(Même remarque que ci-dessus, on pourra avoir des méthodes métiers génériques, par exemple, récupérer l’utilisateur courant)
J’ai seulement implémenté quelques fonctions qui vont nous permettre de récupérer des données.
On pourra aussi utiliser le pattern Specification, qui nous permettra d’ajouter une autre couche d’abstraction pour la récupération de données, pour spécifier les données que nous souhaitons.
Mais cela sera l’objet d’un prochain article !
Reprenons nos créations avec le dbContext, que j’ai nommé CoreDbContext :
CoreDbContext.cs → Projet Infrastructure, dossier Database
public class CoreDbContext : DbContext
{
public CoreDbContext(DbContextOptions<CoreDbContext> options) : base(options)
{ }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
}
}
Par la suite on va pouvoir implémenter nos BaseRepository et BaseService :
BaseRepository.cs → Projet Infrastructure, dossier Base
public class BaseRepository<TEntity, TContext> : IBaseRepository<TEntity>
where TEntity : BaseEntity
where TContext : CoreDbContext
{
protected readonly TContext _dbContext;
public BaseRepository(TContext context)
{
_dbContext = context;
}
public async Task<TEntity> GetById(int id)
{
try
{
return await _dbContext.Set<TEntity>().FindAsync(id);
}
catch (Exception ex)
{
throw new Exception($"Impossible de récupérer l'entité: {ex.Message}");
}
}
public async Task<List<TEntity>> GetAllAsync()
{
try
{
return await _dbContext.Set<TEntity>().AsNoTracking().ToListAsync();
}
catch (Exception ex)
{
throw new Exception($"Impossible de récupérer les entités: {ex.Message}");
}
}
public async Task<List<TEntity>> GetAllAsync(Expression<Func<TEntity, bool>> where)
{
try
{
return await _dbContext.Set<TEntity>().Where(where).AsNoTracking().ToListAsync();
}
catch (Exception ex)
{
throw new Exception($"Impossible de récupérer les entités: {ex.Message}");
}
}
}
BaseService.cs → Projet Application, dossier Base
public class BaseService<TRepository, TEntity> : IBaseService<TEntity>
where TRepository : IBaseRepository<TEntity>
where TEntity : BaseEntity
{
protected readonly TRepository _repository;
public BaseService(TRepository repository)
{
_repository = repository;
}
public async Task<TEntity> GetById(int id)
{
return await _repository.GetById(id);
}
public async Task<List<TEntity>> GetAllAsync()
{
return await _repository.GetAllAsync();
}
public async Task<List<TEntity>> GetAllAsync(Expression<Func<TEntity, bool>> where)
{
return await _repository.GetAllAsync(where);
}
}
On a fini tout ce qui est initialisation des nos fichiers de base ! Ils seront utiles et alimentés tout au long de votre projet !
On peut maintenant commencer à monter notre API !
RDV dans cet article pour continuer ce tuto !
Après avoir créé, structuré et posé les bases Clean Architecture .NET, il est temps de passer à l’API, pas à pas, c’est parti !
Comprendre comment gérer correctement les exceptions est essentiel pour garantir la robustesse de vos applications. Cet article explore les principes fondamentaux de la gestion des exceptions en .NET et vous montre une façon de tout centraliser.
Dans cet article, nous allons voir comment contextualiser vos projets dans l’ETL Talend. La contextualisation consiste à, d’une part, « variabiliser » l’ensemble des paramètres qui sont utilisés dans les jobs (connexion aux bases de données, chemin vers l