Принято считать, что фронтенд просто берёт данные из API и рисует интерфейс.
На практике это быстрый путь к тому,
чтобы внешний контракт начал диктовать архитектуру UI.
В чём проблема
API почти никогда не совпадает с тем,
как думает интерфейс.
Бэкенд может отдавать:
👉 странные названия полей
👉 лишние вложенности
👉 nullable там, где UI ждёт значение
👉 статусы в формате, удобном серверу, а не экрану
Если тащить это напрямую в компоненты,
UI быстро заражается чужой моделью.
Что делает anti-corruption layer
Он ставит прослойку между API и приложением.
Не компонент получает сырой ответ,
а адаптер превращает его в нормальную фронтовую модель.
function mapUserDtoToUser(dto: UserDto): User {
return {
id: dto.user_id,
name: dto.full_name ?? 'Unknown',
isAdmin: dto.role === 'admin',
}
}
Компоненту уже не нужно знать,
как именно бэкенд назвал поле.
Почему это важно
UI становится чище
Компоненты работают с понятной моделью,
а не с набором компромиссов из API.
Изменения API дешевле
Поменялся контракт —
меняем адаптер,
а не ищем
user_id по всему проекту.Меньше бизнес-логики в JSX
Все эти проверки:
user?.profile?.data?.attributes?.name
не должны жить в компоненте.
Компонент должен рендерить состояние,
а не расшифровывать ответ сервера.
Где адаптер особенно нужен
👉 API старое или нестабильное
👉 несколько источников данных
👉 разные форматы одной сущности
👉 сложные статусы и enum
👉 много nullable-полей
Чем грязнее внешний мир —
тем полезнее слой нормализации.
Где можно не усложнять
Если проект маленький,
API стабильное,
а модель почти совпадает с UI —
отдельный слой может быть лишним.
Не нужно строить enterprise там,
где достаточно одной функции рядом с запросом.
Главная мысль
Anti-corruption layer — это не архитектура ради архитектуры.
Это защита UI от чужих компромиссов.
API может быть неудобным, историческим или странным.
Но ваш интерфейс не обязан таким становиться.