Там же, где живет оригинальный GetUser
Положите туда же, где лежит объявленный интерфейс с этим методом
бац, а он лежит на слое логики, потому интерфейс на месте использования рекурсивный импорт
|- user |- errors.go <- вот здесь например |- user.go |- db |- mongo |- sql |- mock
Бизнес-модель (Структуры, интерфейсы, реализации сервисов)
ну это вопрос организации папок в проекте, а если у человека организована как: |- handlers |- user |- role |- service |- user |- role |- storage |- user |- role тогда где ошибки?
и тут ещв вопрос: в errors.go там ошибки и стораджа и сервисов? или 1 набор ошибок?
Тогда надо пересмотреть подход к организации кода. Чтобы она перестала отражать склад технических терминов и начала отражать предметную область.
почти все языки с вами поспорят, ну кроме golang и python
Нет. не поспорят. Со мной поспорят разве что Framework Driven Developers
а кстати, а клиентов куда девать? клиенты к Consul/zipkin/microservice ?
а у вас в сторадже может быть ошибка, кроме паники? (ну, или обертки над ней)
обертка над паникой? эт как?
перехваченная паника
в golang можно перехватывать паники?
Куда хотите. Главное чтобы бизнес-код их напрямую не вызывал и ничего о них не знал.
Там ошибки бизнеса. И эти ошибки могут использоваться в любом инфраструктурном слое ниже.
почитал про recover, выглядит интересно, надо попробовать
Не будет циклического импорта. Потому что пакет user не использует пакеты /db/*.
«Как рождались эксепшены в Го»)
мысль интересная. то есть сделать 5 общих бизнесовых ошибок, во всех слоях просто выбрасывать fmt.Errorf(), кроме последнего, а в последнем бизнес слое, заворачивать ошибку в бизнесовую и ее уже анализировать в хендлере. но тут встает вопрос, как отличить ошибку storage, когда запрос не смог выполниться и когда просто не нашлось ничего?
|- user |- user.go <- Тут структуры для функционала работы с ползователями. |- errors.go <- Тут ошибки которые может вернуть сервис / абстрактное хранилище / и т.п. |- service.go <- Тут реализация сервиса, который использует интерфейс хранилища. И которым при работе с пользователями будут пользоваться хэндлеры. |- storage.go <- Тут интерфейс хранилища, который используется сервисом. |- db <- Вот эти ребята реализуют интерфейс который описан в storage.go и возвращают структуры и ошибки которые описаны в user.go и errors.go. |- sql | - mock
ну значит у меня все ок
На этот вопрос нет однозначного ответа. Тут все зависит от фич и от юзкейсов. Это другого уровня вопрос. Поэтому обычно принято начинать с одного пакета в котором хранится вся бизнес-логика (в такой схеме все сущности и сервисы могут лежать в пакете internal например), а уже потом этот пакет дербанить на ограниченные контексты (user/group/etc.) исходя из приобретенных знаний о бизнес-домене. Иначе будет всегда одно и то же. Мы сперва разбили пакеты, потом они почему то должны использовать друг друга и у нас появляются циклические импорты. Потому что оказалось что 2 сущности тесно связаны между собой в рамках одного ограниченного контекста.
вы имеет ввиду когда функционал работает с несколькими сущностями, с ролями и пользователями например?
я думаю Roman спрашивает про слой что-то типа Policy, когда в нем используется UserService и RoleService и OrderService
И да, на самом деле не обязательно использовать одни и те же структуры по всему коду. Код можно дублировать. Если приложение большое, сущностей User может быть несколько в разных пакетах. И эти сущности в каждом пакете будут обозначать немного разные вещи с разными юзкейсами. Существует заблуждение, что условный type User struct уровня бизнес-логики в приложении должен быть один и его потом надо таскать везде где он нужен. Это не так.
ну нет, дублировать код не надо
Вы возводите DRY в абсолют (Т.е. превращаете его в Карго-культ). Когда сущности находятся в разных ограниченных контекстах - дублирование кода сократит вам уйму времени и нервов. Впрочем дело ваше.
не возвожу, ну по крайней мере пока что не замечаю что возвожу
скажут что надо быть дисциплинированным и писать тесты
Причем до изменения первой сущности
Сама необходимость дублирования обычно продиктована ситуациями когда в одном юзкейсе должна (бизнесово) меняться сущность, а во втором нет. 🙂 Т.е. пришел бизнес и сказал:"_ Вот в этой фиче надо вот это поменять." А вы такие: "- Бляяяяяя... А мы не можем, у нас все во всех местах поменяется. Простите." А бизнес такой: "- Погодите, а почему так, у нас же безнесово вещи никак не связаны. Это должно быть просто, почему нет?" А вы такие: "Соррян, у нас DRY"
Поэтому существуют ситуации в которых сущности можно дублировать, чтобы получился "Хороший дизайн" (точнее более гибкий дизайн. Плохой и хороший - не совсем подходящие слова). Но опять таки. Где именно производить дублирование кода, нужно смотреть предметно и точечно, по ситуации.
Так продумать места где система может измениться и есть один из важных этапов проектирования.
ну, скопипастить функционал, много времени не нужно. так что тут описана какая-то невероятная архитектурная проблема. тут, скорее, проблема как раз в другом - копипаст функционала нарушает принцип единой ответственности. а копирование сущностей избегается соответствующими паттернами - интерфейсами или еще какими обертками....
Я бы сказал DRY в части случаев нарушает принцип единой ответственности. Объединяя код у которого имеются разные причины для изменения.
а пон. объединил функционал не зная меры :)
Это только микросервисами решается. А проблема вытекает из неверного выборауровне абстракции скорее всего, потому кажется что похожий функционал это нарушение DRY
Да, я именно об этом. Я лично часто такое встречал. Когда объединяют просто чтобы объединить. Грубо говоря берем CopyPaste детектор и в тупую объединяем похожие строки кода обобщая его
хорошо, что здесь нет джавистов. сейчас бы нас просветили...
я джавист в прошлом
Обсуждают сегодня