avatar
Артём Шумейко
@artemshumeiko
31.03.2026 06:49
Unit of Work — паттерн, который вы (скорее всего) неправильно понимаете

Пару лет назад я наткнулся на одну интересную статью, где довольно подробно разбирался паттерн Unit of Work на примере Flask. Тема показалась мне крутой, и я решил раскрыть её для FastAPI — записал отдельное видео, которое набрало достаточно много просмотров,... а потом мне пришлось его скрыть.

Потому что я понял, что сам же и накосячил с трактовкой паттерна. И что любопытно — до сих пор вижу в проектах, статьях и туториалах ровно ту же ошибку, которую допустил я. Давайте разберёмся, в чём подвох.

Что обычно называют Unit of Work
Чаще всего я вижу примерно такой код, который называют "паттерном UoW":
class UnitOfWork:
def __init__(self):
self.session = async_session_maker()

async def __aenter__(self):
self.users = UserRepository(self.session)
self.orders = OrderRepository(self.session)
return self

async def __aexit__(self, exc_type, *args):
if exc_type:
await self.rollback()
await self.session.close()

async def commit(self):
await self.session.commit()

async def rollback(self):
await self.session.rollback()


И используется это примерно так:
async def create_order(user_id: int, items: list):
async with UnitOfWork() as uow:
user = await uow.users.get(user_id)
order = await uow.orders.create(user, items)
await uow.commit()
return order

Код отлично подходит для создания транзакционности, я сам такое использую. Но называть это Unit of Work — некорректно.

Что такое настоящий Unit of Work
Изначальная трактовка паттерна про другое: UoW накапливает все изменения объектов в памяти, а когда вы закончили — сам разбирается, какие INSERT, UPDATE и DELETE нужно выполнить. То есть это он отвечает именно за отслеживание состояния объектов, а не является обычной обёрткой над транзакцией.

В SQLAlchemy есть встроенная реализация паттерна Unit of Work — Session отслеживает все объекты и при commit() генерирует нужные запросы:
session.add(user)  
# session запомнила: "надо вставить user"
user.name = "Артём"
# session запомнила: "надо обновить user"
session.delete(order)
# session запомнила: "надо удалить order"
session.commit()
# session сгенерировала INSERT, UPDATE, DELETE

То, что описано в комментариях в коде, алхимия делает под капотом именно через паттерн unit of work. (код самого uow я не показываю, если что — можете найти примеры или первоисточники)

Но на практике я не так часто встречал, чтобы именно этот механизм использовался. Гораздо чаще вижу работу через классический session.execute(), когда мы просто экзекьютим какой-то запрос, а не используем ORM-магию алхимии с отслеживанием объектов (всякие add, delete, flush, refresh).

Как тогда называть наш класс?
Я предпочитаю называть такой класс как DBManager или TransactionManager, т.к. это честнее отражает суть — это удобная прослойка для управления транзакциями и доступа к репозиториям, не более.

Правда преимущества такого подхода никуда не деваются от правильного названия:
— можно шарить одну транзакцию между разными репозиториями и сервисами приложения
— удобный доступ к репозиториям без миллиона импортов (просто uow.users.get() вместо импорта класса/экземпляра репозитория каждый раз from some.other.folder.repo.user import UserRepository или дичи по типу помещения нужных репозиториев внутрь __init__ сервиса)
— явный контроль над тем, когда делать commit

Вывод
Если честно, не так важно, как этот класс называется в вашем коде — главное, что вы понимаете, зачем он нужен и какие преимущества даёт. Но понимать разницу между настоящим паттерном UoW и обёрткой над транзакцией всё-таки стоит, чтобы на собесе не попасть в неловкую ситуацию. Хотя, справедливости ради, про Unit of Work спрашивают примерно раз в тысячу лет.

А у вас как называется этот класс в проектах? И вообще, используете такую прослойку или работаете с сессиями напрямую?

emoji — использую похожую обертку
👍 — работаю с сессиями напрямую
👍 64
emoji 35
17
🔥 3
43 88 15.6K

Обсуждение 43

Обсуждение не доступно в веб-версии. Чтобы написать комментарий, перейдите в приложение Telegram.

Обсудить в Telegram