Использовался Laravel sail для контейнеризации.
Товары / количество денег в кошельке / пользователи, которым они принадлежат генерируются фабриками/сидером (db:seed).
Для регулирования прав доступа пользователя к товарам используется политика. Она написана так, что смотреть товары может любой пользователь (и даже неаутентифицированный), для создани товара нужно аутентифицироваться. Изменять и удалять можно только свои товары.
Аутентификация реализована с помощью токенов (Laravel sanctum) При аутентификации выдается токен, который потом нужно использовать для запросов, требующих авторизации.
В историю записываются имена пользователей, название товара и текущая стоимость - в виде строк, без отношений, т.к. Товар могут удалить, пользователь тоже может удалиться и т.п.
Весь процесс покупки завернут в транзакцию, что должно исключить какие-либо конфликты.
Для дополнительной подстраховки на уровне БД такие поля как баланс кошелька, количество товара - имеют тип unsignedInteger, что должно предотвратить невалидную сделку.
Для листинга товаров используется кэширование на Redis. Кэш сбрасывается при обновлении/удалении товаров.
В репозитории присутствует коллекция Postman для тестирования api.
Что касается вопроса "что может пойти не так при 1000 покупок в секунду...."
Долго думал об этом, но возможно я уже предусмотрел эти моменты в своей реализации.
Будет много транзакций в очереди БД (допустим). Если владелец товара в этот момент поменяет цену, то этот запрос окажется где-то в очереди. Те, кто успеют - купят по старой цене, кто нет - будут покупать по новой.
Если проблема в этом, то это тогда вопрос, а как должно быть?
Если даже вознрикнет какая-либо Race condition, целостности данных, на мой взгляд, ничего не угрожает, просто кто-то останется "при своём".
Метод lockForUpdate() не даёт изменить данные продукта во время транзакции.