Что значит "проектировать систему"
Проектирование системы -- это принятие серии решений о том, как организовать компоненты, данные и процессы для достижения бизнес-целей. Каждое решение -- это компромисс между противоречивыми требованиями.
Фундаментальные противоречия
| Требование A | vs | Требование B |
|---|---|---|
| Консистентность | vs | Доступность (CAP) |
| Простота | vs | Гибкость |
| Скорость разработки | vs | Производительность |
| Надежность | vs | Стоимость |
| Масштабируемость | vs | Сложность |
Задача инженера -- найти оптимальный баланс для конкретной ситуации.
Категории подходов
1. Масштабирование (Scaling)
Как система обрабатывает рост нагрузки.
<?php
declare(strict_types=1);
/**
* Two fundamental scaling approaches
*/
// Vertical Scaling: bigger machine
// Simple, but has a ceiling
// Good for: databases, single-leader systems
// Limit: largest available machine
// Horizontal Scaling: more machines
// Complex, but virtually unlimited
// Good for: stateless services, read replicas
// Challenge: data distribution and coordination
/**
* Stateless service: easy to scale horizontally
* Each request is self-contained
*/
final class StatelessApiHandler
{
public function __construct(
private readonly UserRepository $users,
private readonly TokenValidator $tokens,
) {}
public function handleRequest(Request $request): Response
{
// All state comes from the request or external storage
// No local state = any server can handle any request
$token = $request->getHeader('Authorization');
$userId = $this->tokens->validate($token);
$user = $this->users->findById($userId);
return new Response(json_encode($user));
}
}
2. Балансировка нагрузки (Load Balancing)
Как распределять трафик между серверами.
Ключевые алгоритмы:
- Round Robin -- по кругу
- Weighted Round Robin -- с учетом мощности
- Least Connections -- к наименее загруженному
- Consistent Hashing -- стабильное распределение
- IP Hash -- привязка клиента к серверу
3. Кэширование (Caching)
Как ускорить доступ к данным за счет хранения копий в быстрой памяти.
Уровни кэширования:
Браузер (L1) → CDN (L2) → Reverse Proxy (L3) → Application (L4) → Database (L5)
4. Хранение данных (Data Storage)
Как организовать данные для эффективного чтения и записи.
| Подход | Когда использовать |
|---|---|
| SQL | Структурированные данные, транзакции |
| NoSQL (Document) | Гибкая схема, read-heavy |
| NoSQL (Key-Value) | Кэширование, сессии |
| NoSQL (Column) | Time-series, analytics |
| NoSQL (Graph) | Связи между сущностями |
5. Репликация и шардирование (Replication & Sharding)
Как распределить данные для надежности и производительности.
<?php
declare(strict_types=1);
/**
* Replication vs Sharding - fundamental difference
*/
// Replication: same data on multiple nodes
// Purpose: availability and read performance
// Every replica has ALL the data
// Sharding: different data on different nodes
// Purpose: write performance and storage capacity
// Each shard has PART of the data
/**
* Example: determining which shard holds user data
*/
final class ShardRouter
{
/** @var array<\PDO> */
private array $shards;
public function __construct(array $shards)
{
$this->shards = $shards;
}
public function getShardForUser(string $userId): \PDO
{
// Simple hash-based sharding
$shardIndex = crc32($userId) % count($this->shards);
return $this->shards[$shardIndex];
}
}
6. Консистентность (Consistency)
Как обеспечить согласованность данных в распределенной системе.
- Strong consistency -- все читают одинаковые данные
- Eventual consistency -- данные станут согласованными со временем
- Causal consistency -- сохраняется причинно-следственный порядок
7. Event-Driven архитектура
Как компоненты взаимодействуют через события, а не прямые вызовы.
<?php
declare(strict_types=1);
/**
* Synchronous vs Event-driven communication
*/
// Synchronous: direct call, wait for response
// + Simple to understand
// + Immediate feedback
// - Tight coupling
// - Cascading failures
// Event-driven: publish event, consumers react
// + Loose coupling
// + Independent scaling
// + Resilient to failures
// - Eventual consistency
// - Harder to debug
/**
* Event-driven order processing
*/
final class OrderCreatedEvent
{
public function __construct(
public readonly string $orderId,
public readonly string $userId,
public readonly float $total,
public readonly \DateTimeImmutable $createdAt,
) {}
}
// Different services react to the same event independently
// OrderService publishes OrderCreated
// InventoryService: reserves items
// PaymentService: processes payment
// NotificationService: sends confirmation email
// AnalyticsService: updates metrics
8. Отказоустойчивость (Resilience)
Как система продолжает работать при сбоях компонентов.
Ключевые паттерны:
- Circuit Breaker -- прекращение вызовов к упавшему сервису
- Retry with backoff -- повторные попытки с увеличивающейся паузой
- Bulkhead -- изоляция сбоев в одном компоненте
- Timeout -- ограничение времени ожидания
- Fallback -- альтернативный путь при сбое
Как выбрать подход
Дерево решений
<?php
declare(strict_types=1);
/**
* Decision tree for choosing design approaches
*/
final class DesignDecisionTree
{
public function recommend(SystemRequirements $req): array
{
$recommendations = [];
// Scaling decision
if ($req->peakQps > 1000) {
$recommendations[] = 'Horizontal scaling with load balancer';
}
if ($req->peakQps > 10000) {
$recommendations[] = 'CDN for static content';
$recommendations[] = 'Caching layer (Redis)';
}
if ($req->peakQps > 100000) {
$recommendations[] = 'Database sharding';
$recommendations[] = 'Message queues for async processing';
}
// Consistency decision
if ($req->requiresStrongConsistency) {
$recommendations[] = 'Single-leader replication';
$recommendations[] = 'Synchronous writes';
} else {
$recommendations[] = 'Multi-leader or leaderless replication';
$recommendations[] = 'Async processing where possible';
}
// Availability decision
if ($req->availabilityTarget >= 99.99) {
$recommendations[] = 'Multi-region deployment';
$recommendations[] = 'Active-active configuration';
$recommendations[] = 'Circuit breakers on all external calls';
}
return $recommendations;
}
}
Принцип KISS
Начинайте с простейшего решения, которое удовлетворяет текущим требованиям. Усложняйте только когда простое решение перестает работать.
Эволюция типичной системы:
| Этап | Пользователей | Архитектура |
|---|---|---|
| MVP | 100 | Один сервер, SQL |
| Рост | 10K | + Cache, Read replicas |
| Масштаб | 100K | + LB, Horizontal scaling, CDN |
| Зрелость | 1M+ | + Sharding, Microservices, MQ |
Как изучать подходы
Рекомендуемый порядок
- Масштабирование -- фундамент всего
- Кэширование -- самый эффективный способ улучшить производительность
- Балансировка -- необходима для горизонтального масштабирования
- Репликация и шардирование -- для роста данных
- Консистентность -- для корректности
- Event-Driven -- для сложных систем
- Отказоустойчивость -- для production-ready систем
Каждый из этих подходов подробно рассмотрен в следующих главах.
Выводы
Проектирование систем -- это не выбор одного "правильного" подхода, а комбинация нескольких подходов, оптимизированная под конкретные требования. Понимание каждого подхода и его trade-offs -- ключевой навык системного инженера.