В чем разница между паттернами Repository и Facade?

«В чем разница между паттернами Repository и Facade?» — вопрос из категории Паттерны, который задают на 25% собеседований C# Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Repository и Facade — это структурные паттерны, но они решают принципиально разные задачи.

Паттерн Repository

Назначение: Инкапсулирует логику доступа к данным, выступая в роли абстракции над хранилищем (БД, внешний API, файловая система). Он отделяет бизнес-логику от деталей работы с источниками данных.

Пример на C#:

// Абстракция репозитория
public interface IUserRepository
{
    User GetById(int id);
    void Add(User user);
    IEnumerable<User> Find(Expression<Func<User, bool>> predicate);
}

// Конкретная реализация
public class SqlUserRepository : IUserRepository
{
    private readonly AppDbContext _context;
    public SqlUserRepository(AppDbContext context) => _context = context;

    public User GetById(int id)
    {
        // Детали доступа к БД скрыты здесь
        return _context.Users.FirstOrDefault(u => u.Id == id);
    }
    // ... другие методы
}

Почему используется: Позволяет легко подменять источник данных (например, для тестов использовать InMemory-репозиторий) и централизует запросы (например, спецификации).

Паттерн Facade

Назначение: Предоставляет простой, унифицированный интерфейс к сложной подсистеме, скрывая её внутренние зависимости и сложность.

Пример на C#:

// Сложная подсистема
public class PaymentService { public void Process(Payment p) { /* ... */ } }
public class InventoryService { public void Reserve(Order o) { /* ... */ } }
public class NotificationService { public void SendReceipt(Order o) { /* ... */ } }

// Фасад, упрощающий взаимодействие
public class OrderProcessingFacade
{
    private readonly PaymentService _payment;
    private readonly InventoryService _inventory;
    private readonly NotificationService _notification;

    public OrderProcessingFacade(PaymentService p, InventoryService i, NotificationService n)
    {
        _payment = p; _inventory = i; _notification = n;
    }

    public bool PlaceOrder(Order order)
    {
        // Клиент вызывает один метод вместо трёх
        _inventory.Reserve(order);
        _payment.Process(order.Payment);
        _notification.SendReceipt(order);
        return true;
    }
}

Почему используется: Уменьшает связность клиентского кода с подсистемой, делает код чище и проще для использования.

Ключевые отличия

Аспект Repository Facade
Предмет работы Данные (сущности, агрегаты). Действия и операции сложной подсистемы.
Основная цель Абстракция над источником данных. Упрощение интерфейса к набору классов.
Типичный результат Возвращает доменные объекты или коллекции. Часто выполняет операцию без возврата значения или возвращает упрощённый результат.
Уровень применения Уровень доступа к данным (DAL). Уровень координации сервисов или модулей.