Gateway — единый вход: TLS, аутентификация, маршруты, лимиты. BFF — фасад под конкретный клиент. Оба полезны на краю. Оба становятся точкой отказа, если в них переезжает бизнес-логика.
Проектирование сервисов architecture собеседование
Клиенты не ходят в каждый под напрямую. Иначе CORS, N отдельных аутентификаций, смена внутренних URL и нет единого rate limit.
Gateway — дверь. Терминация TLS. Проверка JWT или сессии. Маршрутизация. Иногда грубая агрегация и кэш GET. Ingress в Kubernetes, Envoy, nginx, облачный LB — варианты одной роли.
Backend for Frontend — другой смысл. Подогнать DTO и оркестрацию под Web, iOS или партнёрский API.
Пример. Мобилка не хочет те же 15 полей, что админка. И не хочет сама собирать три сервиса. BFF режет overfetch и прячет внутренние модели. Это не замена доменным сервисам.
Что делает gateway
- TLS, HTTP/2, иногда mTLS до mesh.
- Authn на входе: JWT, сессия, API-ключ. Это не отменяет проверку полномочий внутри сервиса.
- Rate limit и WAF — дёшево отсечь мусор до приложения.
- Маршрутизация по пути и заголовкам, иногда канарейка по заголовку ревизии.
- Таймауты и корреляционный id. Без них «200 на gateway и 30 с внутри» не собираются в картину.
BFF и толстый край
BFF оркестрирует два-три вызова под экран, мапит поля, режет payload. Бизнес-инвариант («нельзя списать дважды») остаётся в доменном сервисе.
Несколько BFF на разные клиенты нормальны. Один BFF «на все экраны мира» снова становится монолитом на краю.
package gateway
func Auth(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
tok, err := bearer(r)
if err != nil {
http.Error(w, "unauthorized", http.StatusUnauthorized)
return
}
claims, err := parseJWT(tok)
if err != nil {
http.Error(w, "unauthorized", http.StatusUnauthorized)
return
}
r = r.WithContext(withUser(r.Context(), claims))
next.ServeHTTP(w, r) // billing всё равно проверяет userID
})
}
Край проверяет вход и маршрутизирует. Инвариант списания остаётся за сервисом.
Отказ и доверие
Падение gateway — падение продукта, если другого входа нет. Нужны реплики, health, лимиты CPU и FD, деградация: читать из кэша, отключить тяжёлую агрегацию.
Проверка JWT только на gateway без проверки сервисом нарушает zero-trust. Обошли mesh — сервис верит всем. Часто проверяют и на краю (дёшево), и внутри (полномочия на ресурс).
Пример. Таймаут на gateway 30 с, у сервиса 2 с. Клиент видит 504, а внутри хендлер ещё пишет в БД. Или наоборот: gateway обрывает, заказ создан, клиент думает, что нет, и жмёт ещё раз.
Типичная ошибка
Сложить бизнес-правила в nginx или Envoy, потому что так быстрее доставить фичу. Через год никто не знает, где инвариант. Вторая ловушка — разные таймауты на краю и внутри: клиент видит ошибку, работа в БД уже идёт.
На собеседовании
Нарисуйте клиент → gateway/BFF → сервисы. Край даёт TLS, лимит, маршруты. Сервис сам проверяет авторизацию на ресурс и идемпотентность. BFF — фасад под клиент, не второй монолит и не замена БД.