Имел я вашу утиную типизациюПриходит ко мне на днях ученик и рассказывает эмоциональную историю об интерфесах в го и о том, как они чуть не свели его с ума.
Началось всё буднично. Он спокойно делал домашку и выстраивал слоистую архитектуру проекта. Вот тебе api-слой с хендлерами, за ним идёт сервисный слой с основной бизнес-логикой и дальше репо-слой для работы с базой данных.
Как и учат в умных книжках, слои были изолированы друг от друга. А как это сделать в гошке? Конечно же заюзав интерфесы. Микросервис был не сложный, типичный CRUD. Поэтому интерфейсы для слоя с бизнес логикой и репо слоя вышли такие:
type UserService interface {
Create(ctx context.Context, name string) (int64, error)
Get(ctx context.Context, id int64) (model.User, error)
}
type UserRepository interface {
Create(ctx context.Context, name string) (int64, error)
Get(ctx context.Context, id int64) (model.User, error)
}
Интерфейсы называются по разному, а содержание одинаковое. Проблемы в этом нет, но и самое интересно ещё впереди.
По моим заветам он намутил простенький DI-контейнер. Для инициализации сервисного слоя нужно прокинуть готовый объект репо слоя. И кайфово же сделать это, как цепную реакцию. Мы вызываем гетер сервисного слоя, тот при создании вызывает гетер репо слоя и так далее. Подробнее об этом тут.
type provider struct {
repo repo.UserRepo
service service.UserService
}
func NewProvider() *provider {
return &provider{}
}
func (p *provider) Repo() repo.UserRepo {
if p.repo == nil {
p.repo = repo.NewUserRepo()
}
return p.service // ВОТ ТУТ И КОСЯК
}
func (p *provider) Service() service.UserService {
if p.service == nil {
p.service = service.New(p.Repo())
}
return p.service
}
Сюда и прокрался косяк. В методе
Repo() он возвращает объект сервиса. Во-первых, это логически не верно, но ошибки компиляции нет, так как интерфейсы репо и сервиса совпадают по методам, а то что назваются по-разному утиной типизации плевать. Во-вторых, объект сервиса на этот момент
nil.
В итоге приложуха запускается, а паникует только при вызове ручек. А чел сидит и понять не может что не так. Всё с дебагом прошерстил, проверил этапы запуска, вызова и так далее. Просидел несколько часов на нервах и чисто случайно заметил этот микро-косяк.
А вы натыкались на какие-нить приколы языка, которые вводили в замешательство?