Модифікувати раніше розроблений веб-додаток «Музичний портал» з монолітною архітектурою таким чином, щоб отримати трирівневу (three-layer) архітектуру:
- Presentation Layer — контролери та представлення (Views).
- Business Logic Layer — сервіси (буде в наступних завданнях).
- Data Access Layer (DAL) — рівень доступу до даних (реалізується в цьому завданні).
Необхідно реалізувати Data Access Layer, зокрема:
- Створити класи-репозиторії для роботи з сутностями бази даних.
- Забезпечити взаємодію з репозиторіями через абстракцію
IRepository<T>для досягнення слабкої зв’язаності (low coupling). - Застосувати патерн Unit of Work, який:
- Спрощує роботу з кількома репозиторіями.
- Гарантує, що всі репозиторії використовують один і той же контекст даних (
DbContext).
| Компонент | Призначення |
|---|---|
| IRepository | Загальний інтерфейс репозиторію (CRUD-операції та інші загальні методи). |
| Repository | Базова реалізація репозиторію на основі Entity Framework Core. |
| Specific repositories | Окремі репозиторії (наприклад, IArtistRepository, ArtistRepository), якщо потрібні спеціальні запити. |
| IUnitOfWork | Інтерфейс, що надає доступ до всіх репозиторіїв та методу SaveChangesAsync(). |
| UnitOfWork | Реалізація патерну: тримає один DbContext і повертає екземпляри репозиторіїв. |
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(); // для спеціальних запитів
}public interface IUnitOfWork : IDisposable
{
IRepository<Artist> Artists { get; }
IRepository<Album> Albums { get; }
IRepository<Song> Songs { get; }
// Додати інші репозиторії за потреби
Task<int> SaveChangesAsync();
}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();
}
}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).