Skip to content

👺 profile: сетевой доступ в UI-компоненте в обход data-слоя — raw fetch в pokemon-card-profile #186

@JsPowWow

Description

@JsPowWow

Почему создана

При аудите фичи 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.

Проблематика

export class PokemonCardProfileComponent implements OnInit {
  public readonly pokemonName = input.required<string>();
  protected readonly pokemonCardData = signal<PokemonDetailApiData | null>(
    null,
  );

  public async ngOnInit() {
    try {
      const response = await fetch(`/mocks/${this.pokemonName()}.json`);
      // ...
      const result: PokemonDetailApiData =
        (await response.json()) as PokemonDetailApiData;
      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-29async 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/фасада) — данные тянутся прямо из компонента

Варианты решения

  1. Вынести в data-слой фичи + фасад (целевой, как опция 1 в 🔴 pokemon-card: HttpClient в компоненте + подписка без takeUntilDestroyed #119). Создать profile/data/api/<entity>/services с методом загрузки карточки, маппинг ApiData → Model в конвертере, состояние — в фасаде на сигналах; компонент получает данные через input()/фасад. Плюсы: соответствует архитектуре, тестируемо, переиспользуемо, заодно поднимает отсутствующий data/-слой. Минусы: объём больше одной правки, а данные пока mock-овые.
  2. Переиспользовать существующую инфраструктуру. Заменить ручной 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    AI TechDebtTechnical debt surfaced by the AI codebase auditscope:profileAudit finding located in the profile feature/layer

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions