Маркетплейс, а не стримінговий сервіс. Різниця має значення архітектурно.
Оператор не будував стримінгового сервісу, де артисти завантажують контент заради охоплення, а виручка приходить пізніше як частка від реклами. Артисти володіють своїм контентом. Слухачі за нього платять. Платформа — це маркетплейс: лістинги товарів, кошик, оформлення замовлення, історія замовлень, звіти про продажі. Бізнес-модель живе в цьому потоці.
Ця відмінність перебудовує архітектуру. Найскладніша проблема стримінгового сервісу — пропускна здатність прийому й затримка відтворення. Найскладніша проблема маркетплейсу — комерційна поверхня: одночасні операції з кошиком, узгодженість замовлень, оформлення замовлення, що не втрачає покупку, якщо фоновий воркер повільний. Зробіть їх неправильно — і ви втратите реальні транзакції від артистів, які й так скептично ставляться до нової платформи.
Оператор також мав план щодо мобільного клієнта — iOS і Android, побудованих пізніше — і хотів, щоб API-рівень був готовий до нього. Не доуставлений згодом. Готовий із самого початку, із версіонованими DTO-керованими ендпоінтами й чітким розділенням Admin/Mobile. Мобільний застосунок не вийшов у проміжку 2022 року. API-рівень вийшов.
Бриф: повна комерційна поверхня для каталогу, яким володіє артист, асинхронне ядро, що не блокує оформлення замовлення на фоновій роботі, і API-рівень, який не доведеться переосмислювати, коли з'явиться мобільний клієнт.
Два рівні, шина повідомлень і три сховища з різними завданнями.
Система розділилася на два застосунки Symfony із першого дня. Монорепо — адмінка й вебінтерфейс — обробляло керування профілями артистів, лістинги товарів, завантаження артворку й бек-офіс оператора. API-рівень було побудовано окремо, як DTO-версіонований REST-сервіс із просторами імен маршрутів /Mobile/v1/ і /Admin/v1/, 96 мобільних ендпоінтів і 130 адмін-ендпоінтів, задокументований і готовий до мобільного клієнта, коли б він не з'явився.
Потоки кошика й замовлення йдуть через Symfony Messenger, а не через синхронні записи. AddToCartController диспетчеризує повідомлення; обробник опрацьовує мутацію кошика асинхронно. Записи замовлень — Order і ProductOrder, з кількістю й загальною вартістю, що відстежуються для кожного рядка — слідують тому самому патерну. Оформлення замовлення не блокується на записах телеметрії, обробці артворку чи будь-чому іншому, що йде на шині. Операційна база даних лишається швидкою, бо віяння записів кероване.
Три сховища, кожне з єдиною відповідальністю. PostgreSQL — це канонічний стан: 25 сутностей, 40+ індексів на гарячих таблицях — користувачі, товари, замовлення, позиції кошика, жанри, теги, зв'язки артист-товар. Redis веде як брокер черги Messenger, так і шар кешу для гарячого читання. InfluxDB зберігає телеметрію дій користувачів. Високочастотні поведінкові події ніколи не торкаються операційної бази даних.
Профілі артистів і описи товарів — назви альбомів, назви треків, мітки жанрів, новини — перекладні на рівні схеми через KnpLabs DoctrineBehaviors. Додавання локалі — це міграція даних, а не зміна коду. Вебінтерфейс використовував Plyr для відтворення аудіо й відео; CropperJS обробляв обрізання й завантаження артворку безпосередньо в інтерфейсі.
- F · 01Кошик і оформлення замовлення через Messenger
- Мутації кошика й опрацювання замовлень ідуть через Symfony Messenger, а не через синхронні записи. Оформлення замовлення підтверджує негайно; шина обробляє віяння записів.
- F · 02Дворівнева архітектура
- Монорепо (адмінка й вебінтерфейс) і окремий DTO-версіонований API-сервіс із просторами імен /Mobile/v1/ і /Admin/v1/ — 96 мобільних ендпоінтів і 130 адмін-ендпоінтів — побудований ще до того, як з'явився мобільний клієнт.
- F · 03Розділення на три сховища
- PostgreSQL для канонічного стану (25 сутностей, 40+ індексів), Redis для брокера черги й кешу, InfluxDB для телеметрії дій користувачів. Кожне сховище має одне завдання.
- F · 04Перекладний контент на рівні схеми
- KnpLabs DoctrineBehaviors на описах товарів, профілях артистів, мітках жанрів і новинах. Додавання локалі — це міграція, а не зміна коду.
- F · 05Конвеєр телеметрії InfluxDB
- Події дій користувачів маршрутизуються через обробники Messenger до InfluxDB. Конвеєр спроєктований для повної телеметрії відтворення, перемотки й пропуску без втручання в операційну базу даних.
- F · 06Асинхронне опрацювання замовлень
- Сутності Order і ProductOrder відстежують покупки з кількістю й вартістю для кожного рядка. Мутації замовлень течуть через шину повідомлень, щоб комерційні операції не конкурували одна з одною.
Кошик іде через шину. API існує раніше за застосунок, що його викликатиме.
Рішення маршрутизувати мутації кошика через Symfony Messenger, а не через синхронні записи, було не про масштаб. Воно було про коректність. Операції з кошиком у маркетплейсі залучають перевірки складу, валідацію стану товару й обробку одночасних сесій. Винесення їх на шину повідомлень відв'язує підтвердження для користувача від віяння записів. Слухач отримує відповідь; платформа наздоганяє. Операційна база даних ніколи не конкурує з комерційною поверхнею.
Той самий принцип переноситься на відтворення. PlayFileHandler і PlayedFileService відв'язують події відтворення від операційної бази даних. Телеметрія дій користувачів пишеться в InfluxDB через шину повідомлень — загалом 21 обробник по всьому API-рівню. Конвеєр InfluxDB спроєктований для повної телеметрії відтворення, перемотки й пропуску; підключення цих додаткових подій — це наступний крок, який оператор може зробити, нічого не переархітектуруючи.
Три сховища — це відповідь, коли кожне сховище заслуговує своє місце, виконуючи роботу, у якій воно найкраще — і нічого більше.
API-рівень було навмисно зроблено із запасом для 2022 року. /Mobile/v1/ існував із 96 ендпоінтами ще до того, як було написано хоча б рядок коду мобільного застосунку. Мобільний клієнт не вийшов у проміжку 2022 року, але оператор успадкував API-сервіс, що вже був версіонований, задокументований і відокремлений від адмін-поверхні. Вартість зробити це пізніше — із живим продуктом, реальними даними користувачів і схемою, що відхилилася — не порівнянна з вартістю зробити це один раз на старті.
Вісім місяців. Три репозиторії. Комерційна поверхня, яку оператор може розширювати.
Вісім місяців активної розробки. 494 коміти у трьох репозиторіях — монорепо, API-рівень і мінімальний лендинг на HTML/Gulp. Система вийшла з повною комерційною поверхнею «артист–слухач»: лістинги товарів, кошик, оформлення замовлення, історія замовлень і звіти про продажі. Асинхронна архітектура — шина Messenger, брокер Redis, конвеєр телеметрії InfluxDB — була у продакшні з першого публічного релізу.
Мобільний клієнт був єдиною запланованою поверхнею, що не вийшла у проміжку 2022 року. API-рівень, побудований, щоб його прийняти, незмінний. Простір імен /Mobile/v1/, DTO-контракти, обробка автентифікації — усе це чекає на клієнта, а не навпаки.
Архітектура, яку успадкував оператор, має три чіткі межі: Postgres для стану, Redis для черги й кешу, InfluxDB для часових рядів. Кожне може відмовити, бути оновленим чи масштабованим незалежно. Наступному інженеру не потрібна шестигодинна орієнтація. Цікаву проблему — асинхронну комерційну поверхню — розв'язано. Усе після цього — продуктова робота.
Ми будували під мобільний клієнт, перш ніж він з'явився. Коли він вийде, платформа цього не помітить.
