Skip to main content
ModernAPI

Pattern Library

Design patterns used throughout the template with examples

ReferenceIntermediate• 20 min read
Repository Pattern
Encapsulates data access logic and provides a consistent interface for data operations

Interface Definition

public interface IUserRepository : IRepository<User, Guid>
{
    Task<User?> GetByEmailAsync(string email, CancellationToken cancellationToken = default);
    Task<bool> IsEmailUniqueAsync(string email, Guid? excludeUserId = null, CancellationToken cancellationToken = default);
    Task<IReadOnlyList<User>> GetActiveUsersAsync(CancellationToken cancellationToken = default);
}

Implementation

public class UserRepository : Repository<User, Guid>, IUserRepository
{
    public UserRepository(ApplicationDbContext context) : base(context) { }
    
    public async Task<User?> GetByEmailAsync(string email, CancellationToken cancellationToken = default)
    {
        return await Context.Users
            .FirstOrDefaultAsync(u => u.Email == email, cancellationToken);
    }
    
    public async Task<bool> IsEmailUniqueAsync(string email, Guid? excludeUserId = null, CancellationToken cancellationToken = default)
    {
        return !await Context.Users
            .AnyAsync(u => u.Email == email && u.Id != excludeUserId, cancellationToken);
    }
}

Benefits

  • • Centralizes data access logic
  • • Makes testing easier with mocking
  • • Provides consistent query interfaces
  • • Supports dependency injection
Unit of Work Pattern
Maintains a list of objects affected by a business transaction and coordinates changes

Interface & Implementation

public interface IUnitOfWork : IDisposable
{
    IUserRepository Users { get; }
    IRefreshTokenRepository RefreshTokens { get; }
    
    Task<int> SaveChangesAsync(CancellationToken cancellationToken = default);
    Task BeginTransactionAsync(CancellationToken cancellationToken = default);
    Task CommitTransactionAsync(CancellationToken cancellationToken = default);
    Task RollbackTransactionAsync(CancellationToken cancellationToken = default);
}

public class UnitOfWork : IUnitOfWork
{
    private readonly ApplicationDbContext _context;
    
    public IUserRepository Users { get; }
    public IRefreshTokenRepository RefreshTokens { get; }
    
    public async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
    {
        await DispatchDomainEventsAsync();
        return await _context.SaveChangesAsync(cancellationToken);
    }
}

Usage Example

public async Task<UserResponse> CreateUserAsync(CreateUserRequest request)
{
    var user = new User(request.Email, request.DisplayName);
    
    await _unitOfWork.Users.AddAsync(user);
    await _unitOfWork.SaveChangesAsync(); // Saves all changes atomically
    
    return _mapper.MapToUserResponse(user);
}