Skip to content

Migrate Catalog (Categories/Services/Tags) to Result errors; remove Services; physical reorg - #72

Closed
evertonschuster wants to merge 2 commits into
pr/02-auth-result-and-shared-error-infrafrom
pr/03-catalog-category-foundation
Closed

Migrate Catalog (Categories/Services/Tags) to Result errors; remove Services; physical reorg#72
evertonschuster wants to merge 2 commits into
pr/02-auth-result-and-shared-error-infrafrom
pr/03-catalog-category-foundation

Conversation

@evertonschuster

Copy link
Copy Markdown
Owner

Summary

Split out of #69 (part 3 — see that PR for the full picture). Stacked on #71 (Auth + shared Result/error infra) — this PR's base branch is #71's, so its diff here shows only what this PR adds on top.

This PR is larger than the <100-file target the other three PRs in this stack hit — deliberately, see "Why this couldn't be split further" below.

  • Categories, Services, and Tags all move to the Result-based error convention docs/adr/014 establishes: domain entities' create() methods, mappers, and API repositories return Result<T, AppError> instead of throwing; useAsync.ts (already Result-based, landed in Migrate Auth to Result-based error handling; add shared Result/error infra #71) is the one hook every feature's data layer builds on now.
  • app/composition/container.ts's CatalogFacade drops the use-case-class indirection (ListCategories/CreateTag/etc. as separate classes) for direct repository delegation ({ execute: repo.method }) — there's no orchestration between the facade and the repository, so the extra class per operation wasn't earning its keep. The 24 now-orphaned use-case-class files (application/use-cases/{categories,services,tags}/) are deleted.
  • Services' frontend implementation (ServicesPage, ServiceForm, six ServicesPage.*.test.tsx files, all its components/hooks/models) is fully removed, reverting /services to a placeholder page (app/pages/ServicesPage/ServicesPage.tsx) — this vertical goes back to stub status, see docs/STATUS.md.
  • Categories moves to a routed create/edit dialog (features/catalog/presentation/categories/pages/CategoriesListPage/, .../CategoryEditorDialog/) per docs/adr/012, replacing the old flat CategoriesPage.tsx/useCategories.ts/CategoryEditorDialog.tsx shape.
  • Tags gets the equivalent internal move (hooks/useTagEditor.ts, pages/TagEditorDialog.tsx) and its own Result migration (Tag.ts/tagMapper.ts/ApiTagRepository.ts) — Tags itself is not being removed here, just migrated; its removal is the next PR in this stack (docs/adr/016).
  • shared/: AuthenticatedHttpClient's get/post/put/delete now return Result<T, AppError> instead of throwing; DeleteConfirmationDialog takes entityName/entityType instead of a raw title/description pair; useCreateInline is removed (no longer used once Services — its only consumer — is gone).
  • Also removes .husky/pre-commit (dangling once .lintstagedrc.json is gone) — the same removal Remove local git hooks; add GetCategoryById endpoint; backend API response/CORS infra #70 makes independently; whichever of the two PRs merges first, the other's identical deletion is a no-op.

Why this couldn't be split further

I initially tried a narrower "Category-only foundation" PR (~99 files) deferring Services/Tags. That failed a real build: app/composition/container.ts wires TagRepository with its new method signature directly (tagRepository.listAll(options) instead of the old (tenantContext, options) two-arg form) — not just a return-type change useAsync-style, but the interface itself. Making that build without also migrating TagRepository/ApiTagRepository/tagMapper/Tag.ts for real isn't a smaller wrapper shim — it's the same size of work as just finishing the migration, since there's no reduced version of an interface signature. Categories, Services, and Tags share container.ts's catalog wiring, router.tsx, and AuthenticatedHttpClient tightly enough that they're one atomic, verified-buildable unit at this layer — mirroring why Auth couldn't be split from Catalog either, just one layer down.

Test plan

  • npm install + npm run build --workspace=apps/admin-frontend — green
  • npm run lint --workspace=apps/admin-frontend — clean, 0 warnings
  • npm run format:check --workspace=apps/admin-frontend — clean
  • npm run test --workspace=apps/admin-frontend — 368/368 passing
  • scripts/sync_agent_skills.py --check, scripts/check_agent_governance.py, scripts/architecture_guard.py — all pass

🤖 Generated with Claude Code

…ervices vertical; physical reorg

Split out of #69 (part 3 of 4 — see that PR for the full picture).
Stacked on #71 (Auth + shared Result/error infra).

This PR is larger than the <100-file target used for the other three
PRs in this stack, deliberately — see "Why this couldn't be split
further" below.

- Categories, Services, and Tags all move to the Result-based error
  convention docs/adr/014 establishes: domain entities' create() methods,
  mappers, and API repositories return Result<T, AppError> instead of
  throwing; useAsync.ts (already Result-based, landed in #71) is the one
  hook every feature's data layer builds on now.
- app/composition/container.ts's CatalogFacade drops the use-case-class
  indirection (ListCategories/CreateTag/etc. as separate classes) for
  direct repository delegation (`{ execute: repo.method }`) - there's no
  orchestration between the facade and the repository, so the extra
  class per operation wasn't earning its keep. The 24 now-orphaned
  use-case-class files (application/use-cases/{categories,services,tags}/)
  are deleted.
- Services' frontend implementation (ServicesPage, ServiceForm, six
  ServicesPage.*.test.tsx files, all its components/hooks/models) is
  fully removed, reverting `/services` to a placeholder page
  (app/pages/ServicesPage/ServicesPage.tsx) - this vertical is going
  back to `stub` status, see docs/STATUS.md.
- Categories moves to a routed create/edit dialog
  (features/catalog/presentation/categories/pages/CategoriesListPage/,
  .../CategoryEditorDialog/) per docs/adr/012, replacing the old flat
  CategoriesPage.tsx/useCategories.ts/CategoryEditorDialog.tsx shape.
- Tags gets the equivalent internal move (hooks/useTagEditor.ts,
  pages/TagEditorDialog.tsx) and its own Result migration
  (Tag.ts/tagMapper.ts/ApiTagRepository.ts) - Tags itself is not
  being removed here, just migrated; its removal is a later PR in
  this stack (docs/adr/016).
- shared/: AuthenticatedHttpClient's get/post/put/delete now return
  Result<T, AppError> instead of throwing; DeleteConfirmationDialog
  takes entityName/entityType instead of a raw title/description pair;
  useCreateInline is removed (no longer used once Services - its only
  consumer - is gone).
- Also removes .husky/pre-commit (dangling once .lintstagedrc.json is
  gone) — the same removal #70 makes independently; whichever of the
  two PRs merges first, the other's identical deletion is a no-op.

## Why this couldn't be split further

I initially tried a narrower "Category-only foundation" PR (~99 files)
deferring Services/Tags. That failed a real build: app/composition/
container.ts wires `TagRepository` with its *new* method signature
directly (`tagRepository.listAll(options)` instead of the old
`(tenantContext, options)` two-arg form) - not just a return-type
change useAsync-style, but the interface itself. Making that build
without also migrating TagRepository/ApiTagRepository/tagMapper/Tag.ts
for real isn't a smaller wrapper shim - it's the same size of work as
just finishing the migration, since there's no reduced version of an
interface signature. Categories, Services, and Tags share
container.ts's catalog wiring, router.tsx, and AuthenticatedHttpClient
tightly enough that they're one atomic, verified-buildable unit at this
layer - mirroring why Auth couldn't be split from Catalog either,
just one layer down.

## Test plan

- [x] `npm install` + `npm run build --workspace=apps/admin-frontend` — green
- [x] `npm run lint --workspace=apps/admin-frontend` — clean, 0 warnings
- [x] `npm run format:check --workspace=apps/admin-frontend` — clean
- [x] `npm run test --workspace=apps/admin-frontend` — 368/368 passing
- [x] `scripts/sync_agent_skills.py --check`, `scripts/check_agent_governance.py`, `scripts/architecture_guard.py` — all pass

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 2, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 973ce092-2ee1-4817-9d35-abd1875bba2a

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

The previous commit deleted .husky/pre-commit (a dangling reference once
.lintstagedrc.json was gone), but check_branch_agnostic_precommit() still
expected that file to exist with specific content, so architecture_guard.py
was failing on this branch's own committed state.

Replaces check_branch_agnostic_precommit with check_local_git_hooks_absent
(verifying the hooks/tooling stay removed instead of checking removed-file
content) and drops husky/lint-staged from package.json - the same fix #70
makes independently on its own branch; whichever PR merges first, the
other's identical change is a no-op.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@evertonschuster

Copy link
Copy Markdown
Owner Author

Fechada automaticamente quando a branch base (#71) foi mergeada e removida. Recriada como #75 a partir do main atualizado — mesmo conteúdo, mesma justificativa de tamanho, sem mudança funcional.

evertonschuster added a commit that referenced this pull request Aug 2, 2026
…ervices; physical reorg (#75)

* Migrate Catalog (Categories/Services/Tags) to Result errors; remove Services vertical; physical reorg

Split out of #69 (part 3 of 4 — see that PR for the full picture).
Recreated from origin/main after #70 and #71 merged, since this repo's
convention (and the split-large-coderabbit-pr skill) is a sequential
series, not stacking on an unmerged branch — CodeRabbit also doesn't
review PRs whose base isn't the default branch, so stacking silently
skipped review for this and the next PR in the series. Same content as
the original #72, just re-based; no functional change.

This PR is larger than the <100-file target used for the other PRs in
this series, deliberately — see "Why this couldn't be split further"
below.

- Categories, Services, and Tags all move to the Result-based error
  convention docs/adr/014 establishes: domain entities' create() methods,
  mappers, and API repositories return Result<T, AppError> instead of
  throwing; useAsync.ts (already Result-based, landed in #71) is the one
  hook every feature's data layer builds on now.
- app/composition/container.ts's CatalogFacade drops the use-case-class
  indirection (ListCategories/CreateTag/etc. as separate classes) for
  direct repository delegation (`{ execute: repo.method }`) - there's no
  orchestration between the facade and the repository, so the extra
  class per operation wasn't earning its keep. The 24 now-orphaned
  use-case-class files (application/use-cases/{categories,services,tags}/)
  are deleted.
- Services' frontend implementation (ServicesPage, ServiceForm, six
  ServicesPage.*.test.tsx files, all its components/hooks/models) is
  fully removed, reverting `/services` to a placeholder page
  (app/pages/ServicesPage/ServicesPage.tsx) - this vertical is going
  back to `stub` status, see docs/STATUS.md.
- Categories moves to a routed create/edit dialog
  (features/catalog/presentation/categories/pages/CategoriesListPage/,
  .../CategoryEditorDialog/) per docs/adr/012, replacing the old flat
  CategoriesPage.tsx/useCategories.ts/CategoryEditorDialog.tsx shape.
- Tags gets the equivalent internal move (hooks/useTagEditor.ts,
  pages/TagEditorDialog.tsx) and its own Result migration
  (Tag.ts/tagMapper.ts/ApiTagRepository.ts) - Tags itself is not
  being removed here, just migrated; its removal is a later PR in
  this stack (docs/adr/016).
- shared/: AuthenticatedHttpClient's get/post/put/delete now return
  Result<T, AppError> instead of throwing; DeleteConfirmationDialog
  takes entityName/entityType instead of a raw title/description pair;
  useCreateInline is removed (no longer used once Services - its only
  consumer - is gone).
- Also removes .husky/pre-commit and fixes architecture_guard.py's
  precommit check accordingly (already merged independently via #70;
  included here too since this branch's own ancestry needed it before
  #70 existed).

## Why this couldn't be split further

I initially tried a narrower "Category-only foundation" PR (~99 files)
deferring Services/Tags. That failed a real build: app/composition/
container.ts wires TagRepository with its *new* method signature
directly (tagRepository.listAll(options) instead of the old
(tenantContext, options) two-arg form) - not just a return-type
change useAsync-style, but the interface itself. Making that build
without also migrating TagRepository/ApiTagRepository/tagMapper/Tag.ts
for real isn't a smaller wrapper shim - it's the same size of work as
just finishing the migration, since there's no reduced version of an
interface signature. Categories, Services, and Tags share
container.ts's catalog wiring, router.tsx, and AuthenticatedHttpClient
tightly enough that they're one atomic, verified-buildable unit at this
layer - mirroring why Auth couldn't be split from Catalog either,
just one layer down.

## Test plan

- [x] `npm install` + `npm run build --workspace=apps/admin-frontend` — green
- [x] `npm run lint --workspace=apps/admin-frontend` — clean, 0 warnings
- [x] `npm run format:check --workspace=apps/admin-frontend` — clean
- [x] `npm run test --workspace=apps/admin-frontend` — 368/368 passing
- [x] `scripts/sync_agent_skills.py --check`, `scripts/check_agent_governance.py`, `scripts/architecture_guard.py` — all pass

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* Fix categories-mobile.spec.ts's mock for the GET-by-id endpoint

useCategoryEditor fetches its own category via GET /api/v1/categories/{id}
(docs/adr/013), but this spec's route mock matched any /api/v1/categories*
path and always returned the full list array regardless of whether the
request was for the collection or a single id - so the by-id fetch
received an array instead of a CategoryDto, and the edit dialog's Nome
field never populated. Mock now inspects the last path segment and
returns the matching single category (404 if not found) for a by-id GET,
the full list otherwise.

Verified against the real Playwright suite (production build + preview,
matching CI): all 10 e2e specs pass, including this one.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
@evertonschuster
evertonschuster deleted the pr/03-catalog-category-foundation branch August 2, 2026 15:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant