Клиент-серверная архитектура
Клиент-серверная архитектура — модель распределённого приложения, где клиент инициирует запросы, а сервер обрабатывает их и возвращает ответы.
Зачем нужно
Это базовая модель, на которой работает весь веб. Понимание ролей клиента и сервера, их разделения ответственности помогает принимать архитектурные решения: что вычислять на клиенте, что на сервере, где хранить данные, как обеспечить безопасность.
Где используется
- Веб-приложения (браузер = клиент, Node.js/Python/Java = сервер)
- Мобильные приложения (iOS/Android = клиент, REST API = сервер)
- Настольные приложения с удалённым бэкендом
- Любое сетевое взаимодействие: email (IMAP/SMTP), FTP, игры
Модель запрос-ответ
Клиент (браузер) Сервер (Node.js)
| |
|------ HTTP Request ------->|
| GET /api/users |
| Host: example.com |
| Authorization: Bearer... |
| | → БД запрос
| | ← Данные из БД
|<----- HTTP Response -------|
| 200 OK |
| Content-Type: json |
| [{id:1, name:...}] |
Разделение ответственности
Клиент отвечает за:
// UI — отображение данных
function renderUsers(users) {
const list = document.getElementById('user-list');
list.innerHTML = users.map(u => `<li>${u.name}</li>`).join('');
}
// UX — интерактивность, валидация формы
document.getElementById('email').addEventListener('input', e => {
const valid = /^[^@]+@[^@]+$/.test(e.target.value);
e.target.setCustomValidity(valid ? '' : 'Некорректный email');
});
// Состояние — что показывать пользователю (loading/error/data)
Сервер отвечает за:
// Бизнес-логика и валидация (обязательно на сервере)
app.post('/api/users', async (req, res) => {
const { email, role } = req.body;
// Безопасность — никогда не доверяем клиенту
if (!isValidEmail(email)) {
return res.status(422).json({ error: 'Invalid email' });
}
// Авторизация — только сервер решает, что разрешено
if (role === 'admin' && !req.user.isAdmin) {
return res.status(403).json({ error: 'Forbidden' });
}
// Работа с БД — клиент не имеет прямого доступа
const user = await db.users.create({ email, role: 'viewer' });
res.status(201).json(user);
});
Варианты архитектуры
Монолит:
Browser ──────────────► Node.js + Express (HTML + API + БД)
SPA + API:
Browser ──────────────► Nginx (static SPA)
└─────────────► Node.js API ──► PostgreSQL
Микросервисы:
Browser ──────────────► API Gateway
├──► User Service ──► User DB
├──► Order Service ──► Order DB
└──► Notification Service ──► Redis
SSR (Next.js):
Browser ◄── HTML ◄────► Next.js Server (рендер + API)
└──► Backend API / БД
Принцип разделения
Клиент (не доверяем): Сервер (доверяем):
- Никогда не хранить - Хранить секреты
секреты (API keys, - Проверять права доступа
DB passwords) - Валидировать данные
- Клиентская валидация - Применять бизнес-правила
— только UX - Работать с БД напрямую
- Кэшировать UI-данные - Генерировать токены
Частые ошибки
- Хранение секретов (API ключей, паролей) в клиентском коде — они видны всем
- Доверять клиентской валидации — злоумышленник может отправить любые данные
- Реализация бизнес-логики только на клиенте — легко обойти
- Прямой доступ браузера к БД без API-слоя — критическая уязвимость