You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
При аудите фичи profile (ветка develop) в PokemonCardProfileComponent обнаружен прямой сетевой доступ из UI-компонента: fetch() в ngOnInit, разбор JSON и обработка ошибки — всё внутри компонента. Дополнительно у фичи profileполностью отсутствует слой data/ (0 файлов) — в отличие от main-catalog/auth/frenzy, где есть data/api + фасады.
Это «сестра» уже заведённого #119 (PokemonCardComponent в main-catalog делает то же через HttpClient). Классы совпадают (обход слоёв data/api + facade), но scope другой и здесь нет утечки подписки — fetch/Promise, а не Observable.
Проблематика
exportclassPokemonCardProfileComponentimplementsOnInit{publicreadonlypokemonName=input.required<string>();protectedreadonlypokemonCardData=signal<PokemonDetailApiData|null>(null,);publicasyncngOnInit(){try{constresponse=awaitfetch(`/mocks/${this.pokemonName()}.json`);// ...constresult: PokemonDetailApiData=(awaitresponse.json())asPokemonDetailApiData;this.pokemonCardData.set(result);}catch(error){console.error("Ошибка при загрузке:",error);}}}
По docs/Стайлгайд структура папок.md компонент не ходит в сеть напрямую: «компонент напрямую вызывает API-service, минуя facade» и «бизнес-правила остаются в шаблоне или компоненте страницы» — в списке нежелательных зависимостей; запросы и маппинг ответа должны жить в data/api/<entity>/services + фасаде, а компонент получает готовые данные (route -> page -> ui components -> facade -> services/api -> backend). Сейчас же в UI зашиты транспорт (fetch), формат URL (/mocks/${name}.json), парсинг ответа и обработка ошибки (console.error).
Сопутствующие запахи в том же месте:
async ngOnInit() — Angular игнорирует возвращённый Promise; data-fetch в lifecycle-хуке вместо реактивного источника (httpResource/rxResource/фасад).
console.error как обработка сетевой ошибки в UI-компоненте — ошибка нигде не отражается в состоянии, пользователь её не видит.
В проекте уже есть инфраструктура доступа к покемон-данным (@shared/services/pokemon-data.service.ts, core PokemonApiService), которую карточка переизобретает ручным fetch.
Фича сейчас WIP (placeholder-данные, caughtList/favoritesList пустые, TODO про «список из юзера») — но raw fetch уже лежит на develop, и слой данных стоит завести при следующей итерации, а не наслаивать ad-hoc доступ.
Где
src/app/features/profile/ui/components/pokemon-card-profile/pokemon-card-profile.component.ts:15-29 — async ngOnInit с прямым fetch, парсингом и console.error в компоненте
src/app/features/profile/ui/components/pokemon-card-profile/pokemon-card-profile.component.ts:17 — URL /mocks/${name}.json зашит в UI
фича profile целиком без слоя data/ (нет data/api/фасада) — данные тянутся прямо из компонента
Варианты решения
Вынести в data-слой фичи + фасад (целевой, как опция 1 в 🔴 pokemon-card: HttpClient в компоненте + подписка без takeUntilDestroyed #119). Создать profile/data/api/<entity>/services с методом загрузки карточки, маппинг ApiData → Model в конвертере, состояние — в фасаде на сигналах; компонент получает данные через input()/фасад. Плюсы: соответствует архитектуре, тестируемо, переиспользуемо, заодно поднимает отсутствующий data/-слой. Минусы: объём больше одной правки, а данные пока mock-овые.
Переиспользовать существующую инфраструктуру. Заменить ручной fetch на httpResource/rxResource или @shared/services/pokemon-data.service.ts (детали покемона уже умеет тянуть через httpResource). Плюсы: быстро убирает ручной транспорт, парсинг и console.error, реактивный источник вместо async ngOnInit. Минусы: архитектурный 👺 (данные мимо фасада фичи) частично остаётся; источник /mocks/ отличается от реального API.
Рекомендация
Целевой — вариант 1: он совпадает с уже принятым направлением #119 и закрывает отсутствие data/-слоя у фичи. Учитывая, что profile пока WIP на mock-данных, прагматично сделать его на следующей итерации, когда появятся реальные «пойманные/избранные» из пользователя. До тех пор — минимум вариант 2: убрать raw fetch + console.error из компонента в пользу httpResource/shared-сервиса, чтобы транспорт и парсинг не жили в UI. В любом случае сетевой доступ из компонента в обход слоёв оставаться не должен.
docs/Стайлгайд структура папок.md (нежелательные зависимости; route -> page -> facade -> services/api) · docs/Стайлгайд нейминг и структура.md (Signals/RxJS) · сестра #119
Почему создана
При аудите фичи
profile(веткаdevelop) вPokemonCardProfileComponentобнаружен прямой сетевой доступ из UI-компонента:fetch()вngOnInit, разбор JSON и обработка ошибки — всё внутри компонента. Дополнительно у фичиprofileполностью отсутствует слойdata/(0 файлов) — в отличие отmain-catalog/auth/frenzy, где естьdata/api+ фасады.Это «сестра» уже заведённого #119 (
PokemonCardComponentвmain-catalogделает то же черезHttpClient). Классы совпадают (обход слоёвdata/api+ facade), но scope другой и здесь нет утечки подписки —fetch/Promise, а не Observable.Проблематика
По
docs/Стайлгайд структура папок.mdкомпонент не ходит в сеть напрямую: «компонент напрямую вызывает API-service, минуя facade» и «бизнес-правила остаются в шаблоне или компоненте страницы» — в списке нежелательных зависимостей; запросы и маппинг ответа должны жить вdata/api/<entity>/services+ фасаде, а компонент получает готовые данные (route -> page -> ui components -> facade -> services/api -> backend). Сейчас же в UI зашиты транспорт (fetch), формат URL (/mocks/${name}.json), парсинг ответа и обработка ошибки (console.error).Сопутствующие запахи в том же месте:
async ngOnInit()— Angular игнорирует возвращённый Promise; data-fetch в lifecycle-хуке вместо реактивного источника (httpResource/rxResource/фасад).console.errorкак обработка сетевой ошибки в UI-компоненте — ошибка нигде не отражается в состоянии, пользователь её не видит.В проекте уже есть инфраструктура доступа к покемон-данным (
@shared/services/pokemon-data.service.ts, corePokemonApiService), которую карточка переизобретает ручнымfetch.Фича сейчас WIP (placeholder-данные,
caughtList/favoritesListпустые,TODOпро «список из юзера») — но raw fetch уже лежит наdevelop, и слой данных стоит завести при следующей итерации, а не наслаивать ad-hoc доступ.Где
src/app/features/profile/ui/components/pokemon-card-profile/pokemon-card-profile.component.ts:15-29—async ngOnInitс прямымfetch, парсингом иconsole.errorв компонентеsrc/app/features/profile/ui/components/pokemon-card-profile/pokemon-card-profile.component.ts:17— URL/mocks/${name}.jsonзашит в UIprofileцеликом без слояdata/(нетdata/api/фасада) — данные тянутся прямо из компонентаВарианты решения
profile/data/api/<entity>/servicesс методом загрузки карточки, маппингApiData → Modelв конвертере, состояние — в фасаде на сигналах; компонент получает данные черезinput()/фасад. Плюсы: соответствует архитектуре, тестируемо, переиспользуемо, заодно поднимает отсутствующийdata/-слой. Минусы: объём больше одной правки, а данные пока mock-овые.fetchнаhttpResource/rxResourceили@shared/services/pokemon-data.service.ts(детали покемона уже умеет тянуть черезhttpResource). Плюсы: быстро убирает ручной транспорт, парсинг иconsole.error, реактивный источник вместоasync ngOnInit. Минусы: архитектурный 👺 (данные мимо фасада фичи) частично остаётся; источник/mocks/отличается от реального API.Рекомендация
Целевой — вариант 1: он совпадает с уже принятым направлением #119 и закрывает отсутствие
data/-слоя у фичи. Учитывая, чтоprofileпока WIP на mock-данных, прагматично сделать его на следующей итерации, когда появятся реальные «пойманные/избранные» из пользователя. До тех пор — минимум вариант 2: убрать rawfetch+console.errorиз компонента в пользуhttpResource/shared-сервиса, чтобы транспорт и парсинг не жили в UI. В любом случае сетевой доступ из компонента в обход слоёв оставаться не должен.docs/Стайлгайд структура папок.md(нежелательные зависимости;route -> page -> facade -> services/api) ·docs/Стайлгайд нейминг и структура.md(Signals/RxJS) · сестра #119