Откуда берется информация о реализации репозитория
Что проверяют
Понимаете ли вы, как в Go абстракция соединяется с реализацией. Интерфейс объявлен в одном пакете, структура с методами в другом, и вопрос ровно про то, кто и в какой момент их связывает. Заодно смотрят, различаете ли вы слой доступа к данным и хранилище исходников: звучит вопрос одинаково, а ответы разные, поэтому уточнить контекст это плюс, а не слабость.
Как отвечать
В Go нет ключевого слова implements. Тип реализует интерфейс, если у него есть нужный набор методов, и компилятор проверяет это в точке присваивания:
type UserRepo interface {
ByID(ctx context.Context, id int64) (*User, error)
}
type pgUserRepo struct{ db *pgxpool.Pool }
var _ UserRepo = (*pgUserRepo)(nil) // проверка на этапе компиляцииДальше главное: интерфейс объявляется на стороне потребителя, то есть в пакете сервиса, а не рядом с реализацией. Конкретный тип создаётся один раз при сборке приложения и передаётся конструктором:
svc := service.New(postgres.NewUserRepo(pool))Сервис знает только про интерфейс, а знание о конкретной реализации живёт в main. В рантайме интерфейсная переменная это пара из таблицы методов конкретного типа и указателя на данные, по ней и уходит вызов.
Где спотыкаются
Кладут интерфейс в пакет с реализацией и получают обратную зависимость: сервис тянет за собой драйвер базы, а подменить его в тесте нечем. Интерфейс принадлежит тому, кто его вызывает.
Пишут один интерфейс на двадцать методов «про всё хранилище». Такой тяжело мокать, и каждый новый метод ломает сразу все реализации. Лучше узкие интерфейсы под конкретный сценарий.
Возвращают из конструктора интерфейс вместо конкретного типа. Принято принимать интерфейсы и возвращать структуры: иначе вызывающий теряет остальные методы, а nil-указатель, завёрнутый в интерфейс, перестаёт быть равен nil, и проверка ошибки молча ломается.