Модифікувати раніше розроблений веб-додаток «Кінопошук» («10 найкращих фільмів») з монолітною архітектурою таким чином, щоб отримати трирівневу (three-layer) архітектуру:
- Presentation Layer — контролери, представлення (Views) або Web API ендпоінти.
- Business Logic Layer / Service Layer — сервіси для обробки бізнес-логіки та файлів (наприклад,
IMovieService). - Data Access Layer (DAL) — рівень доступу до даних (реалізується в цьому завданні).
Необхідно реалізувати Data Access Layer, зокрема:
- Створити класи-репозиторії для роботи з сутностями бази даних.
- Забезпечити взаємодію з репозиторіями через абстракцію
IRepository<T>для досягнення слабкої зв’язаності (low coupling). - Застосувати патерн Unit of Work, який:
- Спрощує роботу з кількома репозиторіями.
- Гарантує, що всі репозиторії використовують один і той же контекст даних (
DbContext).
| Компонент | Призначення |
|---|---|
IRepository<T> |
Загальний інтерфейс репозиторію (CRUD-операції та інші загальні методи). |
Repository<T> |
Базова реалізація репозиторію на основі Entity Framework Core. |
| Specific repositories | Окремі репозиторії (наприклад, IMovieRepository, MovieRepository), якщо потрібні спеціальні запити (наприклад, фільтрація за жанром або режисером). |
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<Movie> Movies { get; }
IRepository<Genre> Genres { get; }
IRepository<Director> Directors { get; }
// Додати інші репозиторії за потреби
Task<int> SaveChangesAsync();
}public class UnitOfWork : IUnitOfWork
{
private readonly MovieDbContext _context;
public IRepository<Movie> Movies { get; private set; }
public IRepository<Genre> Genres { get; private set; }
public IRepository<Director> Directors { get; private set; }
public UnitOfWork(MovieDbContext context)
{
_context = context;
Movies = new Repository<Movie>(context);
Genres = new Repository<Genre>(context);
Directors = new Repository<Director>(context);
}
public async Task<int> SaveChangesAsync()
{
return await _context.SaveChangesAsync();
}
public void Dispose()
{
_context.Dispose();
}
}builder.Services.AddDbContext<MovieDbContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
builder.Services.AddScoped<IUnitOfWork, UnitOfWork>();public class MovieService : IMovieService
{
private readonly IUnitOfWork _unitOfWork;
private readonly IWebHostEnvironment _environment;
public MovieService(IUnitOfWork unitOfWork, IWebHostEnvironment environment)
{
_unitOfWork = unitOfWork;
_environment = environment;
}
public async Task<IEnumerable<Movie>> GetAllMoviesAsync()
{
return await _unitOfWork.Movies.GetAllAsync();
}
public async Task CreateMovieAsync(Movie movie)
{
await _unitOfWork.Movies.AddAsync(movie);
await _unitOfWork.SaveChangesAsync();
}
}- Слабка зв’язаність — сервіси та контролери залежать лише від абстракцій (
IUnitOfWork,IRepository<T>). - Єдиний контекст — патерн Unit of Work гарантує використання одного
DbContextу межах одного HTTP-запиту. - Покращена тестованість — легко мокувати
IUnitOfWorkдля написання юніт-тестів. - Масштабованість — зручна підготовка проєкту до трансформації в Web API та підключення SPA-фронтенду (React).
- Відповідність найкращим практикам — чітке розділення шарів та використання класичних патернів Repository + Unit of Work.