Repository Pattern & Unit of Work

লেসন ৩৪ / ৫০

Repository Pattern কী?

Data access logic-কে business logic থেকে আলাদা করার pattern — DB code change করলে controller ভাঙবে না।

Generic Repository Interface

public interface IRepository<T> where T : class {
    Task<T?> GetByIdAsync(int id);
    Task<IEnumerable<T>> GetAllAsync();
    Task<IEnumerable<T>> FindAsync(Expression<Func<T, bool>> predicate);
    Task AddAsync(T entity);
    void Update(T entity);
    void Delete(T entity);
}

Implementation

public class Repository<T> : IRepository<T> where T : class {
    protected readonly AppDbContext _ctx;
    protected readonly DbSet<T> _set;

    public Repository(AppDbContext ctx) {
        _ctx = ctx;
        _set = ctx.Set<T>();
    }

    public async Task<T?> GetByIdAsync(int id) => await _set.FindAsync(id);
    public async Task<IEnumerable<T>> GetAllAsync() => await _set.ToListAsync();
    public async Task<IEnumerable<T>> FindAsync(Expression<Func<T, bool>> p) => await _set.Where(p).ToListAsync();
    public async Task AddAsync(T entity) => await _set.AddAsync(entity);
    public void Update(T entity) => _set.Update(entity);
    public void Delete(T entity) => _set.Remove(entity);
}

Unit of Work ⭐

একাধিক repository-কে একই transaction-এ commit/rollback করার pattern।

public interface IUnitOfWork : IDisposable {
    IRepository<Product> Products { get; }
    IRepository<Order> Orders { get; }
    Task<int> SaveChangesAsync();
}

public class UnitOfWork : IUnitOfWork {
    private readonly AppDbContext _ctx;
    public IRepository<Product> Products { get; }
    public IRepository<Order> Orders { get; }

    public UnitOfWork(AppDbContext ctx) {
        _ctx = ctx;
        Products = new Repository<Product>(ctx);
        Orders = new Repository<Order>(ctx);
    }

    public Task<int> SaveChangesAsync() => _ctx.SaveChangesAsync();
    public void Dispose() => _ctx.Dispose();
}

// Program.cs
builder.Services.AddScoped<IUnitOfWork, UnitOfWork>();

Service Usage

public class OrderService {
    private readonly IUnitOfWork _uow;
    public OrderService(IUnitOfWork uow) => _uow = uow;

    public async Task PlaceOrderAsync(Order order) {
        await _uow.Orders.AddAsync(order);
        var product = await _uow.Products.GetByIdAsync(order.ProductId);
        product.Stock -= order.Qty;
        _uow.Products.Update(product);
        await _uow.SaveChangesAsync();   // single transaction
    }
}

Pros & Cons

  • ✅ Testable — mock IRepository
  • ✅ Swap DB easily
  • ❌ EF Core নিজেই DbContext = UoW + Repository pattern, তাই over-abstraction হতে পারে