Всем привет!
У меня есть бекенд, написан на FastApi. Проект преставляет собой p2p обменник. Существует роутер который вносит изменения в ордер, этот ордер вызывает один из методов OrderService класса. Каждый ордер имеет 3 стадии:
- Создания
- Оплата на карту со стороны покупателя
- Перевод крипты со стороны продовца
Пример проблемы хочу привести с помощью ситуации, когда продавец отправляет запрос на перевод средств покупателю, а то есть, мы находимся на 3 этапе ордера.
Моя проблема заключается именно в архитектуре и логике класса OrderService. Начнем с листинга метода который я вызываю
link.
Коротко объясню что происходит, в первых строках это блокировка чтобы другие методы немогли одновременно редактировать тот же самый ордер, дальше или вызывается метод _done который завершает ордер или метод _dispute который открывае спор, после этого уведомления через телеграмм для контрагентов. Нас интересует метод _done
link.
В описании функции указываются этапы которые она делает, дальше нас интересует метод transfer класса WalletService
link.
Наконец-то мы добрались до последнего метода, теперь можем приступить к проблеме которая мне не дает покоя. В этом методе происходит логика перевода, все что нам нужно знать, это то что происходят запросы к базе данных, опять в начале функции открываются блокировщики чтобы другие методы не изменяли в одночасье тежи самые кошельки с которыми мы сейчас взаимодействуем.
Проблема: в каждой функции мы видим как передается параметр uow: UnitOfWork, который представляет собой транзакцию в базе данных, идея в том, чтобы в начале роутера открывать транзакцию к базе данных (async with uow) а в конце роутера коммитить все изменения (await uow.commit()) и закрывать транзакцию, если вдруг в течении выполнения роутера выскакивает исключение то код не доходит до коммита и при выходе с оператора with запускается метод rollback() который стирает все незакоммиченные изменения к базе данных. Теперь ближе к проблеме, вы можете видеть в конце метода transfer вызов await uow.commit(), он вызывается так как нужно выйти с блокировщика и дать другим запросам к этим же кошелькам исполниться без ошибок и с учетом данных изменений, но если вызывать uow.commit() в методе transfer то ломается логика коммита в конце, когда все исполниться без ошибок, в таком случае нужно коммитить каждый шаг до транзакции, чтобы не произошло такого, что после успешной транзакции, где-то в коде вылетит исключение и все незакоммиченные изменения до и после транзакции сотрутся, а транзакция останется. Пока что в коде нету изменений которые вносятся в базу данных до перевода, но в будущем я думаю оно будет и мне очень не нравится что это не очевидно, что при добавлении куда-либо метода перевода или другого метода класса WalletService мне прийдется следить и коммитить каждый этап до и после транзакции. Я конечно понимаю что коммитить после каждого важного этапа в методе можно, но проблема в том что я не представляю как это отсматривать в коде и тем более контролировать.
Надеюсь я смог донести проблему. Извиняюсь за неопытность :). И заранее спасибо за любой ответ!
Буду благодарен и ссылкам для чтения.