Как эффективно структурировать проект на ASP.NET Core для уменьшения дублирования кода?

Я начал изучать ASP.NET Core и его организацию проектов, и у меня возник вопрос. <br/><br/> У меня есть сущности User, Book и Order, для которых я создаю репозитории, сервисы и UseCases. Наблюдается, что в каждом репозитории повторяются аналогичные CRUD операции для всех сущностей: получение всех пользователей/книг/заказов, поиск по ID, добавление и удаление. <br/><br/> Аналогично, логика в сервисах и UseCases также часто пересекается. Как можно устранить дублирование кода и правильно организовать крупный проект на ASP.NET Core?
  • 7 декабря 2024 г. 16:33
Ответы на вопрос 3
Структурирование проекта на ASP.NET Core с целью уменьшения дублирования кода — это важный аспект разработки, который позволяет упростить поддержку и расширяемость приложения. Вот несколько подходов, которые помогут вам организовать проект более эффективно:

### 1. Использование Generics для репозиториев

Одним из наиболее распространенных способов избежать дублирования кода в репозиториях является использование обобщённых (generic) классов. Вы можете создать базовый репозиторий, который будет реализовывать общие CRUD операции.

```csharp
public interface IRepository<TEntity> where TEntity : class
{
    Task<IEnumerable<TEntity>> GetAllAsync();
    Task<TEntity> GetByIdAsync(int id);
    Task AddAsync(TEntity entity);
    Task UpdateAsync(TEntity entity);
    Task DeleteAsync(int id);
}

public class Repository<TEntity> : IRepository<TEntity> where TEntity : class
{
    private readonly DbContext _context;
    private readonly DbSet<TEntity> _dbSet;

    public Repository(DbContext context)
    {
        _context = context;
        _dbSet = context.Set<TEntity>();
    }

    public async Task<IEnumerable<TEntity>> GetAllAsync() => await _dbSet.ToListAsync();
    public async Task<TEntity> GetByIdAsync(int id) => await _dbSet.FindAsync(id);
    public async Task AddAsync(TEntity entity) => await _dbSet.AddAsync(entity);
    public async Task UpdateAsync(TEntity entity) => _dbSet.Update(entity);
    public async Task DeleteAsync(int id)
    {
        var entity = await GetByIdAsync(id);
        if (entity != null)
            _dbSet.Remove(entity);
    }
}
```

С помощью такого подхода, вы можете создать репозитории для конкретных сущностей, наследуя их от базового класса:

```csharp
public class UserRepository : Repository<User>, IUserRepository
{
    public UserRepository(DbContext context) : base(context) { }
}

public class BookRepository : Repository<Book>, IBookRepository
{
    public BookRepository(DbContext context) : base(context) { }
}
```

### 2. Создание базовых сервисов

Аналогично репозиториям, вы можете создать базовый сервис для обработки логики, относящейся к CRUD операциям. Это поможет избежать дублирования в сервисах.

```csharp
public interface IService<TEntity>
{
    Task<IEnumerable<TEntity>> GetAllAsync();
    Task<TEntity> GetByIdAsync(int id);
    Task AddAsync(TEntity entity);
    Task UpdateAsync(TEntity entity);
    Task DeleteAsync(int id);
}

public class BaseService<TEntity> : IService<TEntity> where TEntity : class
{
    private readonly IRepository<TEntity> _repository;

    public BaseService(IRepository<TEntity> repository)
    {
        _repository = repository;
    }

    public Task<IEnumerable<TEntity>> GetAllAsync() => _repository.GetAllAsync();
    public Task<TEntity> GetByIdAsync(int id) => _repository.GetByIdAsync(id);
    public Task AddAsync(TEntity entity) => _repository.AddAsync(entity);
    public Task UpdateAsync(TEntity entity) => _repository.UpdateAsync(entity);
    public Task DeleteAsync(int id) => _repository.DeleteAsync(id);
}
```

Теперь вы можете создать конкретные сервисы для каждой сущности, наследуясь от базового сервиса.

```csharp
public class UserService : BaseService<User>, IUserService
{
    public UserService(IUserRepository repository) : base(repository) { }
}
```

### 3. Использование паттерна "Unit of Work"

Если у вас есть несколько репозиториев, которые работают с одним и тем же контекстом базы данных, вы можете использовать паттерн "Unit of Work", чтобы объединить их в одном классе.

```csharp
public interface IUnitOfWork : IDisposable
{
    IUserRepository Users { get; }
    IBookRepository Books { get; }
    IOrderRepository Orders { get; }
    Task SaveAsync();
}

public class UnitOfWork : IUnitOfWork
{
    private readonly DbContext _context;
    public IUserRepository Users { get; }
    public IBookRepository Books { get; }
    public IOrderRepository Orders { get; }

    public UnitOfWork(DbContext context)
    {
        _context = context;
        Users = new UserRepository(_context);
        Books = new BookRepository(_context);
        Orders = new OrderRepository(_context);
    }

    public async Task SaveAsync() => await _context.SaveChangesAsync();

    public void Dispose() => _context.Dispose();
}
```

### 4. Использование MediatR

Использование MediatR для обработки команд и запросов также может помочь уменьшить дублирование кода в Use Cases. Вмест
Либо делать декомпозицию и писать общие для всех объектов методы либо взять готовое решение, где уже есть всё нужное или хотя бы основная часть.
следуй принципам SOLID, DRY, KISS
Похожие вопросы