В чем заключается паттерн Unit of Work

Ответ

Unit of Work — это поведенческий паттерн проектирования, который отслеживает изменения бизнес-объектов и координирует их запись в базу данных в рамках одной транзакции. Его главная цель — обеспечить атомарность и согласованность данных при выполнении группы операций.

Паттерн решает проблему выполнения нескольких действий (вставка, обновление, удаление) как единого целого. Все изменения накапливаются и применяются к базе данных одним вызовом (commit), либо полностью отменяются (rollback) в случае ошибки.

Ключевые задачи:

  • Отслеживание состояния: Регистрирует новые, измененные и удаленные объекты.
  • Группировка операций: Объединяет все изменения в одну транзакцию.
  • Атомарность: Гарантирует, что либо все изменения будут сохранены, либо ни одно из них.

Многие ORM, например SQLAlchemy, реализуют этот паттерн через объект сессии (Session). Вот пример явной реализации с использованием контекстного менеджера в Python:

# Пример реализации паттерна Unit of Work
class UnitOfWork:
    def __init__(self, session_factory):
        self.session_factory = session_factory

    def __enter__(self):
        # В начале блока with создается новая сессия
        self.session = self.session_factory()
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        # При выходе из блока with
        if exc_type:
            # Если произошло исключение, откатываем транзакцию
            self.session.rollback()
        else:
            # Иначе — успешно коммитим
            self.session.commit()
        self.session.close()

# Использование в сервисном слое
# Session - это сконфигурированная фабрика сессий SQLAlchemy
with UnitOfWork(Session) as uow:
    user = User(name='Alice')
    uow.session.add(user)
    # commit или rollback произойдет автоматически при выходе из блока

Ответ 18+ 🔞

А, ну это ж классика, блядь! Unit of Work, ёпта! Это когда ты, сука, как на рынке, набрал в корзину всякой хуйни — помидоров, огурцов, яблок, а потом подходишь к кассе и одним махом, блядь, всё оплачиваешь. Или, если денег не хватило, такой: «Ой, бля, извините», — и вываливаешь всё обратно на прилавок. Вот и весь паттерн, в рот меня чих-пых!

Суть-то в чём, а? Чтобы не бегать к базе данных на каждый чих: «О, добавил запись!», «О, обновил!», «О, удалил!». Это ж заебёшься, блядь, и базу заебёшь. А тут ты копишь изменения в своей оперативке, как хитрая жопа, а потом — раз! — и отправляешь одним здоровенным пакетом. Либо всё сработает, либо, если где-то косяк, нихуя не сработает. Атомарность, блядь, называется. Всё или ничего. Красота!

Вот смотри, как это в коде выглядит, если самому, на коленке, собрать:

# Пример реализации паттерна Unit of Work
class UnitOfWork:
    def __init__(self, session_factory):
        self.session_factory = session_factory

    def __enter__(self):
        # В начале блока with создается новая сессия
        self.session = self.session_factory()
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        # При выходе из блока with
        if exc_type:
            # Если произошло исключение, откатываем транзакцию
            self.session.rollback()
        else:
            # Иначе — успешно коммитим
            self.session.commit()
        self.session.close()

# Использование в сервисном слое
# Session - это сконфигурированная фабрика сессий SQLAlchemy
with UnitOfWork(Session) as uow:
    user = User(name='Alice')
    uow.session.add(user)
    # commit или rollback произойдет автоматически при выходе из блока

Видишь эту магию? Зашёл в блок with — сессия открылась. Надобавлял туда, наобновлял, наудалял. А как выходишь — оно само смотрит: «Так, чувак, тут всё чисто? Не упало ничего?». Если чисто — коммит, блядь! Если где-то посередине вылетело исключение — роллбэк, и все твои изменения, как корова языком слизала. Ни хуя не осталось. Порядок, а не жизнь!

А в нормальных ORM, типа SQLAlchemy, это вообще из коробки есть. Там этот Session и есть твой Unit of Work, готовый, обкатанный. Но понимать, как эта штука изнутри работает — это, блядь, сила. Чтобы не быть тем самым распиздяем, который в каждом углу свою сессию плодит, а потом охуевает, почему данные не консистентные.