← Назад к вопросам

Что общего между паттернами Repository и Facade?

1.8 Middle🔥 161 комментариев
#ООП и паттерны проектирования

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Сходства паттернов Repository и Facade

Несмотря на то, что Repository (Репозиторий) и Facade (Фасад) решают разные задачи в архитектуре приложения, они имеют несколько фундаментальных общих черт, основанных на принципах проектирования промежуточных слоёв абстракции. Оба паттерна относятся к структурным и широко применяются в C# Backend-разработке для управления сложностью и изоляции частей системы.

1. Оба являются паттернами-обёртками (Wrappers)

Основная общая функция — инкапсуляция сложной логики за простым интерфейсом.

  • Facade упрощает взаимодействие с комплексной подсистемой (например, с несколькими классами для работы с видеоконвертацией или внешним API), предоставляя единый, удобный интерфейс.
  • Repository инкапсулирует всю логику доступа к данным (запросы к БД, кэширование, маппинг) для определённой сущности (Aggregate Root), предоставляя коллекцию-подобный интерфейс.
// Пример Facade: Упрощение работы с подсистемой уведомлений
public class NotificationFacade
{
    private readonly IEmailService _emailService;
    private readonly ISmsService _smsService;
    private readonly IPushService _pushService;

    public NotificationFacade(IEmailService emailService, ISmsService smsService, IPushService pushService)
    {
        _emailService = emailService;
        _smsService = smsService;
        _pushService = pushService;
    }

    // Простой метод, скрывающий сложность рассылки по трём каналам
    public async Task SendNotificationAsync(User user, string message)
    {
        await _emailService.SendAsync(user.Email, message);
        await _smsService.SendAsync(user.Phone, message);
        await _pushService.SendAsync(user.DeviceId, message);
    }
}

// Пример Repository: Инкапсуляция доступа к данным для сущности Product
public interface IProductRepository
{
    Task<Product> GetByIdAsync(int id);
    Task<IEnumerable<Product>> GetByCategoryAsync(string category);
    Task AddAsync(Product product);
    Task UpdateAsync(Product product);
    Task DeleteAsync(int id);
}

2. Снижение связанности (Coupling)

Это ключевая архитектурная цель, которую преследуют оба паттерна.

  • Facade уменьшает связанность клиентского кода с внутренними классами подсистемы. Клиент зависит только от Фасада.
  • Repository, особенно в контексте Domain-Driven Design (DDD), является частью слоя доступа к данным и отделяет доменную модель (бизнес-логику) от инфраструктурных деталей (ORM, база данных). Домен зависит только от абстракции репозитория, а не от конкретной реализации (например, DbContext Entity Framework).

3. Предоставление упрощённого интерфейса

Оба паттерна следуют принципу KISS (Keep It Simple, Stubid) для клиентского кода.

  • Facade заменяет множество вызовов разнородных методов одним или несколькими осмысленными методами высокого уровня.
  • Repository предоставляет интерфейс, который абстрагирует базу данных в виде коллекции объектов. Вместо написания SQL-запросов или сложных LINQ-выражений в бизнес-сервисе, разработчик использует понятные методы типа GetById, Find, Add.

4. Улучшение тестируемости

Благодаря введению слоя абстракции оба паттерна облегчают модульное тестирование.

  • Код, использующий Facade, можно тестировать, подменяя фасад или его зависимости (через DI) моками или стабами.
  • Repository является классическим примером паттерна, который позволяет тестировать бизнес-логику (доменные сервисы, Use Cases) в полной изоляции от базы данных. Достаточно создать мок (Mock<IProductRepository>) или in-memory реализацию интерфейса репозитория.
// Тестирование сервиса, использующего Repository без реальной БД
[Test]
public async Task ProcessOrder_ShouldCallRepository()
{
    // Arrange
    var mockRepo = new Mock<IOrderRepository>();
    var testOrder = new Order { Id = 1, Status = OrderStatus.New };
    mockRepo.Setup(repo => repo.GetByIdAsync(1)).ReturnsAsync(testOrder);

    var orderService = new OrderService(mockRepo.Object);

    // Act
    await orderService.ProcessOrderAsync(1);

    // Assert
    mockRepo.Verify(repo => repo.UpdateAsync(It.IsAny<Order>()), Times.Once);
    Assert.That(testOrder.Status, Is.EqualTo(OrderStatus.Processing));
}

5. Централизация часто изменяемой логики

Изменения в одной части системы локализуются внутри паттерна.

  • Если меняется API внешней подсистемы (например, платёжного гейта), правки вносятся только внутри класса Facade.
  • Если меняется способ доступа к данным (миграция с Dapper на Entity Framework Core, изменение схемы таблицы), изменения ограничиваются реализацией конкретного Repository, в то время как интерфейс и доменная логика могут остаться неизменными.

Сводная таблица сходств

КритерийПаттерн FacadeПаттерн Repository
Основная цельУпрощение интерфейса к сложной подсистемеАбстракция доступа к данным для агрегата
Тип паттернаСтруктурныйАрхитектурный (часто относится к слою данных)
Ключевой принципИнкапсуляция сложностиИнкапсуляция логики доступа к данным
Влияние на связанностьСнижает связанность клиента с подсистемойСнижает связанность домена с инфраструктурой
Улучшение тестируемостиДа, через изоляцию подсистемыДа, через абстрагирование от БД
Использование в C#Классы-обёртки для внешних сервисов, библиотекКлассы, реализующие IRepository<T>, часто с EF Core

Важное заключительное отличие: Несмотря на сходства, нельзя считать Repository частным случаем Facade. Facade — это упрощающая обёртка для неудобного или сложного интерфейса, в то время как Repository — это семантически насыщенная абстракция, которая является частью предметной области и следует определённым контрактам (например, имитация работы с коллекцией). Repository имеет более строгую архитектурную роль, особенно в подходах типа DDD или Clean Architecture.

Что общего между паттернами Repository и Facade? | PrepBro