Вот раньше хорошо было, один монолит, одна БД. Транзациями рулит база данных реализуя ACID, тебе только важно не забывать транзакцию в коде передавать к вызову SQL. А сейчас, в эпоху микросервисов вызовы к бд чередуются с обращением к внешнему API, а внешнему апи всё равно какая там у тебя транзакция в бд.
И вот чтобы это всё хорошо организовать в коде, разделить по слоям, не перемешивать вызовы SQL и HTTP существуют паттерны Repository и Unit of Work. Без них красиво построить 2PC и Саги не получиться (Если интересно почему, жду вопрос в комментарии).
Примеры будут на моей любимой гошке
Паттерн Repository
Паттерн Repository (репозиторий) — это архитектурный паттерн, который предоставляет абстракцию для доступа к данным. Он отделяет логику работы с данными от бизнес-логики приложения, что упрощает тестирование и поддержку кода.
Основные идеи паттерна Repository:
- Абстракция доступа к данным: Репозиторий предоставляет интерфейс для выполнения операций с данными, таких как создание, чтение, обновление и удаление (CRUD). Это скрывает детали реализации, такие как SQL-запросы или взаимодействие с ORM.
- Инкапсуляция логики запросов: Репозиторий инкапсулирует логику построения запросов к базе данных, что позволяет избежать дублирования кода и упрощает его изменение.
- Упрощение тестирования: Благодаря абстракции доступа к данным, можно легко подменять реализацию репозитория на заглушки или моки, что упрощает написание модульных тестов.
Короче простыми словами - не должно быть в слое репозитория бизнес-логики! Сделали круды на таблицы - норм вообще, дальше просто на уровне сервиса будете объединять обращения к таблицам.
Пример использования паттерна Repository на языке Go:
Как правильно описывать методы для Репозитория
При проектировании методов для репозитория важно соблюдать баланс между универсальностью и специфичностью. Вот несколько рекомендаций:
- Избегайте слишком узконаправленных функций: Например, функции, которые выполняют очень специфичные задачи, могут привести к избыточности кода. Вместо этого старайтесь делать функции более универсальными.
- Избегайте сложной бизнес-логики в репозитории: Репозиторий должен быть простым и не содержать сложной бизнес-логики. Это делает код более управляемым и тестируемым.
- Управление транзакциями: Репозиторий не должен управлять транзакциями напрямую. Это задача Unit of Work, который координирует работу нескольких репозиториев.
- Связь полей SQL и сущностей приложения: Используйте метки для полей структур, чтобы связать их с полями в базе данных. Это упрощает создание запросов и внесение изменений.
- Заполнение полей сущности: Избегайте заполнения полей сущности внутри методов репозитория. Это может привести к размытию ответственности между слоями.
Паттерн Unit of Work
Unit of Work (единица работы) — это паттерн, который управляет транзакциями и координирует изменения, которые должны быть сохранены в базе данных. Он отслеживает изменения в объектах и гарантирует, что все изменения будут применены в рамках одной транзакции.
Основные идеи паттерна Unit of Work:
- Управление транзакциями: Unit of Work координирует работу нескольких репозиториев и управляет транзакциями.
- Отслеживание изменений: Он отслеживает изменения в объектах и гарантирует, что только измененные объекты будут сохранены.
- Тестируемость: Обеспечивает единый интерфейс для управления транзакциями, что упрощает тестирование.
Короче простыми словами - создаёшь и управляешь ты транзакцией не на уровне репозитория, а на уровне сервиса. Так ты сможешь вызвать CRUD запросы к БД передавая туда транзацию, а также делать http вызовы. За счет того что это всё будет на уровне сервиса - бизнес логика будет только там, код будет отгранизован хорошо
Пример использования Unit of Work:
Заключение
Заключение я хочу сделать в формате диалога, чтобы точно была понятна суть статьи
Ты: Ну что, как ты думаешь, как эти паттерны Repository и Unit of Work помогают в разработке?
Я: О, это как когда ты строишь дом. Repository — это как фундамент, который отделяет работу с кирпичами и бетоном от всей архитектуры дома. Он скрывает сложности работы с базой данных, так что ты можешь сосредоточиться на том, чтобы дом был красивым и функциональным.
Ты: Интересно! А Unit of Work?
Я: Unit of Work — это как главный строитель, который координирует все работы. Он следит за тем, чтобы все изменения в доме были сделаны правильно и в одной транзакции. Если что-то пошло не так, он может откатить изменения, чтобы все осталось в порядке.
Ты: Звучит удобно! Так что же это значит для нашей разработки?
Я: Это значит, что мы можем строить сложные системы, как микросервисы, без хаоса. Repository упрощает работу с данными, а Unit of Work управляет транзакциями. В итоге, код становится чище, легче тестировать и поддерживать. И это особенно важно, когда у нас есть много разных сервисов и баз данных.
Ты: Понятно! Спасибо за объяснение!
Давай заключим сделку, я продолжаю писать - ты подписываешься на мою телегу https://t.me/timofey_yakunin. Win-Win подход.