Skip to content

Instantly share code, notes, and snippets.

@sunmeat
Created December 17, 2025 13:17
Show Gist options
  • Select an option

  • Save sunmeat/72d5ed8a0d0d7bf66da7292546ab93ed to your computer and use it in GitHub Desktop.

Select an option

Save sunmeat/72d5ed8a0d0d7bf66da7292546ab93ed to your computer and use it in GitHub Desktop.
ДЗ: багаторівнева архітектура, шар доступу до даних

Багаторівнева архітектура в ASP.NET Core MVC

Рівень доступу до даних (Data Access Layer)

Веб-додаток «Музичний портал»

Завдання

Модифікувати раніше розроблений веб-додаток «Музичний портал» з монолітною архітектурою таким чином, щоб отримати трирівневу (three-layer) архітектуру:

  1. Presentation Layer — контролери та представлення (Views).
  2. Business Logic Layer — сервіси (буде в наступних завданнях).
  3. Data Access Layer (DAL) — рівень доступу до даних (реалізується в цьому завданні).

Необхідно реалізувати Data Access Layer, зокрема:

  • Створити класи-репозиторії для роботи з сутностями бази даних.
  • Забезпечити взаємодію з репозиторіями через абстракцію IRepository<T> для досягнення слабкої зв’язаності (low coupling).
  • Застосувати патерн Unit of Work, який:
    • Спрощує роботу з кількома репозиторіями.
    • Гарантує, що всі репозиторії використовують один і той же контекст даних (DbContext).

Структура Data Access Layer

Компонент Призначення
IRepository Загальний інтерфейс репозиторію (CRUD-операції та інші загальні методи).
Repository Базова реалізація репозиторію на основі Entity Framework Core.
Specific repositories Окремі репозиторії (наприклад, IArtistRepository, ArtistRepository), якщо потрібні спеціальні запити.
IUnitOfWork Інтерфейс, що надає доступ до всіх репозиторіїв та методу SaveChangesAsync().
UnitOfWork Реалізація патерну: тримає один DbContext і повертає екземпляри репозиторіїв.

Ключові інтерфейси та класи

1. Загальний репозиторій

public interface IRepository<T> where T : class
{
    Task<T> GetByIdAsync(int id);
    Task<IEnumerable<T>> GetAllAsync();
    Task AddAsync(T entity);
    void Update(T entity);
    void Delete(T entity);
    IQueryable<T> Query(); // для спеціальних запитів
}

2. Unit of Work

public interface IUnitOfWork : IDisposable
{
    IRepository<Artist> Artists { get; }
    IRepository<Album> Albums { get; }
    IRepository<Song> Songs { get; }
    // Додати інші репозиторії за потреби

    Task<int> SaveChangesAsync();
}

3. Реалізація Unit of Work

public class UnitOfWork : IUnitOfWork
{
    private readonly MusicPortalDbContext _context;
    
    public IRepository<Artist> Artists { get; private set; }
    public IRepository<Album> Albums { get; private set; }
    public IRepository<Song> Songs { get; private set; }

    public UnitOfWork(MusicPortalDbContext context)
    {
        _context = context;
        Artists = new Repository<Artist>(context);
        Albums = new Repository<Album>(context);
        Songs = new Repository<Song>(context);
    }

    public async Task<int> SaveChangesAsync()
    {
        return await _context.SaveChangesAsync();
    }

    public void Dispose()
    {
        _context.Dispose();
    }
}

Реєстрація в DI-контейнері (Program.cs)

builder.Services.AddDbContext<MusicPortalDbContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));

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

Використання в контролері (приклад)

public class ArtistController : Controller
{
    private readonly IUnitOfWork _unitOfWork;

    public ArtistController(IUnitOfWork unitOfWork)
    {
        _unitOfWork = unitOfWork;
    }

    public async Task<IActionResult> Index()
    {
        var artists = await _unitOfWork.Artists.GetAllAsync();
        return View(artists);
    }

    [HttpPost]
    public async Task<IActionResult> Create(Artist artist)
    {
        if (ModelState.IsValid)
        {
            await _unitOfWork.Artists.AddAsync(artist);
            await _unitOfWork.SaveChangesAsync();
            return RedirectToAction(nameof(Index));
        }
        return View(artist);
    }
}

Переваги впровадженої архітектури

  • Слабка зв’язаність — контролери та сервіси залежать лише від абстракцій (IUnitOfWork, IRepository<T>).
  • Єдиний контекст — патерн Unit of Work гарантує використання одного DbContext у межах одного запиту.
  • Покращена тестованість — легко мокувати IUnitOfWork для юніт-тестів.
  • Масштабованість — легко додавати нові репозиторії та спеціальні методи.
  • Відповідність найкращим практикам — чітке розділення шарів та використання класичних патернів Repository + Unit of Work.

Ця модифікація перетворює монолітний додаток на добре структурований трирівневий проєкт, готовий до подальшого розширення (наприклад, додавання Business Logic Layer).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment