
Unit of Work в Go: практический гайд по транзакциям между репозиториями
kmoseenk 8 минут назад Unit of Work в Go: практический гайд по транзакциям между репозиториями Средний 14 мин 397 Блог компании OTUS Go * SQL * Туториал Перевод Автор оригинала: rednafi.com Эта статья выросла из пары...
Anthropic — What company has the best second artificial intelligence model at the end of June?
Значимый прорыв формирует отрасль ИИ: kmoseenk 8 минут назад Unit of Work в Go: практический гайд по транзакциям между репозиториями Средний 14 мин 397 Блог компании OTUS Go * SQL * Туториал Перевод Автор оригинала: rednafi. com Эта статья выросла из пары быстрых ответов на вопросы на Reddit в ветке r/golang. Первый был о том, стоит ли делать слой репозиториев поверх sqlc.
Второй — о том, как работать с транзакциями, когда интерфейс скрывает детали хранилища. Оба ответа превратились в небольшие статьи. Здесь я собираю их вместе и разбираю, что делать, когда транзакции должны охватывать несколько репозиториев.
Технические детали
Разберём три этапа, каждый следующий опирается на предыдущий:поставить интерфейс репозитория между сервисной логикой и слоем хранения;добавить поддержку транзакций в один репозиторий, не протаскивая SQL в сервис;координировать транзакции между несколькими репозиториями через Unit of Work. Во всех примерах используется SQLite. Рабочие примеры для варианта с одним хранилищем и для варианта с несколькими хранилищами есть на GitHub.
Что из себя представляет паттерн Репозиторий? Мартин Фаулер в своей книге «Шаблоны архитектуры корпоративных приложений» определял паттерн «Репозиторий» так:«Репозиторий» выступает посредником между доменным слоем и слоем маппинга данных, предоставляя похожий на коллекцию интерфейс для доступа к доменным объектам. В Go репозиторий — это просто интерфейс.
Сервис зависит от интерфейса, конкретный пакет его реализует, и живут они в разных пакетах. Сервис описывает, что ему нужно, а хранилище это предоставляет. Принцип инверсии зависимостей в действии.
Отраслевые последствия
Чтобы понять, зачем это нужно, посмотрим, что происходит без репозитория. Что происходит без репозиторияДопустим, вы пишете сервис книжного магазина с sqlc. Сгенерированный код даёт структуру Queries с методами вроде GetBook и CreateBook.
Самый соблазнительный вариант — внедрить её прямо в сервис:type Service func (s *Service) RegisterBook( ctx context. Context, title string) (db. Book, error) { return s.
CreateBook(ctx, title) }Такой код компилируется и работает, но сервис теперь намертво привязан к сгенерированным типам sqlc. Каждый метод сервиса импортирует пакет db. Если вы захотите протестировать RegisterBook без базы данных, придётся мокать весь Queries или поднимать тестовую базу данных.
Этот прогресс даёт важные сигналы о будущем отрасли, и технологический мир внимательно наблюдает.




