Архітектура e-commerce: масштабування за межі моноліту
Перехід застарілої платформи електронної комерції, яка обробляє мільйони щоденних транзакцій, на мікросервісну архітектуру. Детальний аналіз викликів, стратегій та остаточного успіху розподіленої системи.
Mihail Leybuk-GalinСистемний архітекторПоділитися статтею
1. Точка перегину
Кожен успішний монолітний додаток зрештою досягає переломного моменту. Для нашого клієнта — провідного рітейлера модного одягу — цей момент настав під час піку Чорної п'ятниці 2022 року. Монолітний додаток Ruby on Rails, який добре служив їм протягом п'яти років, вийшов з ладу під постійним навантаженням у 45 000 запитів на секунду.
Симптоми були класичними: вичерпання пулу підключень до бази даних, каскадні тайм-аути та розгортання, які займали понад годину через величезний розмір кодової бази. Було зрозуміло, що горизонтальне масштабування всього додатку більше не було економічно вигідним або технічно достатнім.
2. Деконструкція Моноліту
Перехід не був переписуванням типу «великого вибуху». Ми застосували шаблон Strangler Fig, поступово витягуючи обмежені контексти. Ми почали з сервісу «Cart», оскільки він вимагав високої доступності, але мав відносно мало залежностей від основної платформи.
Стан цільової архітектури (після міграції)
Відкрити повнорозмірне зображенняМаршрутизуючи трафік через новий шлюз API, ми могли динамічно направляти запити для кінцевих точок /cart до нового мікросервісу на базі Go, залишаючи весь інший трафік спрямованим до застарілого моноліту. Це дозволило нам перевірити новий сервіс у продакшені з підмножиною користувачів перед повним переходом.
3. Прийняття парадигм, керованих подіями
Оскільки ми вилучали більше сервісів (Інвентаризація, Виконання замовлень, Профілі користувачів), синхронні REST-виклики між ними призводили до неприйнятної затримки та тісного зв'язку. Ми впровадили Apache Kafka як нашу центральну нервову систему.
order_handler.go
func (h *OrderHandler) ProcessCheckout(ctx context.Context, req CheckoutRequest) error {
// 1. Validate request
if err := h.validator.Validate(req); err != nil {
return err
}
// 2. Create Order Aggregate in local DB (Pending state)
order := NewOrder(req)
if err := h.repo.Save(ctx, order); err != nil {
return err
}
// 3. Emit OrderCreated event to Kafka topic
event := events.OrderCreatedEvent{
OrderID: order.ID,
Items: order.Items,
Total: order.Total,
}
if err := h.eventBus.Publish(ctx, "orders.created", event); err != nil {
// Compensating transaction or DLQ logic here
return fmt.Errorf("failed to publish event: %w", err)
}
return nil
}Переходячи до моделі, керованої подіями, служба замовлень просто генерує подію OrderCreated та негайно повертає користувачеві відповідь про успішне виконання. Служби інвентаризації, оплати та виконання асинхронно споживають цю подію та обробляють відповідну логіку домену.
4. Результати та показники
Міграція тривала 14 місяців, але вплив можна було виміряти вже протягом перших трьох. До Чорної п'ятниці 2023 року нова архітектура була повністю функціональною.
- Досягнуто часу безвідмовної роботи
- 99.99%
- Середня затримка p95
- 65мс
- Збільшення пропускної здатності
- 3.2x
Окрім первинних показників продуктивності, вплив на організацію був суттєвим. Середній час відновлення (MTTR) значно знизився, а продуктові команди могли розгортатися незалежно кілька разів на день, не заважаючи один одному.