Persistență prin EF Core în Infrastructure Layer

  • Doru Bulubasa
  • 06 August 2025

Într-o arhitectură bazată pe principiile Clean Architecture sau DDD (Domain-Driven Design), separarea responsabilităților este esențială. Un aspect important este separarea logicii de acces la date într-un Infrastructure Layer, unde folosim Entity Framework Core pentru a interacționa cu baza de date.

🧩 De ce separăm persistenta în Infrastructure Layer?

  • Menținem codul de business curat și testabil.

  • Permitem ușor testarea logicii folosind mock-uri sau fakes (prin interfețe).

  • Putem schimba motorul de persistență (ex: EF Core → Dapper sau MongoDB) fără să atingem logica de business.


🔧 Pas 1: Definirea unei interfețe comune – IApplicationDbContext

Această interfață va fi folosită în toate zonele care au nevoie să acceseze baza de date, dar fără să cunoască implementarea concretă (adică ApplicationDbContext).

public interface IApplicationDbContext

{

    DbSet<Post> Posts { get; }

    Task<int> SaveChangesAsync(CancellationToken cancellationToken = default);

}

Observă că expunem doar ceea ce e necesar (ex: DbSet-uri și metoda de salvare).


🧱 Pas 2: Implementarea concretă – ApplicationDbContext

Această clasă moștenește DbContext din EF Core și implementează interfața IApplicationDbContext.

public class ApplicationDbContext : DbContext, IApplicationDbContext

{

    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)

        : base(options) { }

    public DbSet<Post> Posts => Set<Post>();

    public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)

        => base.SaveChangesAsync(cancellationToken);

}


🧪 Pas 3: Înregistrarea în DI (Dependency Injection)

services.AddDbContext<ApplicationDbContext>(options =>

    options.UseSqlServer(configuration.GetConnectionString("DefaultConnection")));

services.AddScoped<IApplicationDbContext>(provider => provider.GetRequiredService<ApplicationDbContext>());

Astfel, codul care primește IApplicationDbContext (ex: un CommandHandler) nu știe că folosește EF Core – ceea ce face codul testabil și decuplat.


✍️ Exemplu de utilizare în CreatePostCommandHandler

public class CreatePostCommandHandler : IRequestHandler<CreatePostCommand, Guid>

{

    private readonly IApplicationDbContext _context;

    public CreatePostCommandHandler(IApplicationDbContext context)

    {

        _context = context;

    }

    public async Task<Guid> Handle(CreatePostCommand request, CancellationToken cancellationToken)

    {

        var post = new Post

        {

            Id = Guid.NewGuid(),

            Title = request.Title,

            Content = request.Content

        };

        _context.Posts.Add(post);

        await _context.SaveChangesAsync(cancellationToken);

        return post.Id;

    }

}


✅ Concluzie

Prin definirea unei interfețe (IApplicationDbContext) și folosirea sa în codul de business, păstrăm separarea clară între:

  • logica de business (care nu știe nimic despre EF Core)

  • infrastructura de date (care implementează EF Core)

Această separare este fundamentală pentru scalabilitate, testabilitate și întreținerea pe termen lung.

Scrie un comentariu

Adresa de mail nu va fi publicata. Campurile obligatorii sunt marcate cu *