Replace ~35 organically-grown docs (docs/platform/*, docs/backend-platform/*, one-off sprint reports, Search.md, Diagnostics.md, Content-Management.md, Backend-Handoff-Sprint16.md, docs/superpowers/*, docs/Project-Editor.md, untracked docs/total.md) with the six canonical docs declared in .claude/CLAUDE.md: PROJECT.md, ARCHITECTURE.md, BACKEND.md, FRONTEND.md, BOOTSTRAP.md, EDITOR.md, plus a new PROJECT-STRUCTURE.md. - BACKEND.md is a punch list per domain (auth, bootstrap draft/publish, static pages, categories, products, orders, dashboard metrics, activity, translations, search, product engagement) plus a Known reliability issues section on the prod 502/504 root cause. - ARCHITECTURE.md links to (does not duplicate) the enforced docs/architecture/foundation/** ADRs and standards docs. - docs/ADMIN.md and docs/architecture/foundation/** and docs/context/** are left untouched per instructions. - Updated the one dangling docs/Project-Editor.md reference in admin-auth.service.ts to point at docs/BACKEND.md. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
7.9 KiB
PROJECT STRUCTURE
Folder-by-folder tour of src/app/**, then one worked example (the Sprint 19 admin dashboard) followed as a literal file-by-file walk-through, ending with a checklist for adding your own feature.
Standards referenced below are enforced, not suggestions: docs/architecture/foundation/Folder-Blueprint.md, Naming-Conventions.md, Dependency-Rules.md, Import-Boundary-Matrix.md.
Top-level folders
| Folder | What belongs here | Why |
|---|---|---|
core/ |
Per-domain: DTOs, mappers, domain models, repositories, domain services (e.g. core/categories/, core/products/, core/search/, core/admin-auth/). |
Isolates backend-shaped data (DTOs) from the rest of the app. Only the mapper inside a domain's core/<domain>/ folder is allowed to see both DTO and domain model shapes (ADR-003 import boundaries). |
facades/ |
Cross-feature facades not owned by a single feature, e.g. facades/platform/category.facade.ts, facades/platform/search.facade.ts. |
The only thing components are allowed to inject for data/state (ADR-006/007). Feature-local facades instead live inside that feature's own facade/ folder (see features/project-editor/facade/, features/admin/dashboard/facade/). |
features/ |
One folder per feature/domain: project-editor/, admin/<subfeature>/, backoffice/<subfeature>/, website/catalog/, website/product/, search/, content-management/, diagnostics/. |
Organized by feature, not by file type — a feature's models/services/facade/components/pages all live together (docs/architecture/foundation/Folder-Blueprint.md). |
shared/ |
shared/models/config/* (the BootstrapConfig and ~20 sub-configs), reusable presentational UI, utils. |
Feature-agnostic by contract — shared/ must never import from features/ (Import-Boundary-Matrix). |
widgets/ |
contracts/ (widget manifest contract), registry/ (manifest service), resolvers/ (data-source resolver), ui/ (widget components). |
The dynamic rendering engine — see docs/ARCHITECTURE.md. |
dynamic-renderer/ |
section-engine/, page-renderer/, section-renderer/, widget-host/. |
The page-composition pipeline that turns bootstrap JSON into rendered pages. |
layouts/ |
Page-chrome containers, e.g. layouts/containers/dynamic-page-layout.component.ts. |
Top-level layout composition, one level above pages. |
i18n/ |
translations.ts (interface), en.ts/ru.ts/hy.ts, translate.pipe.ts, TranslateService. |
Single source of truth for all user-facing copy — see docs/FRONTEND.md. |
pages/ |
Top-level routed pages not part of a larger feature module (home, cart, category). |
Simpler routed pages that don't warrant a full features/ module. |
components/ |
Reusable standalone components shared across features/pages (product-card, telegram-login). |
Presentational, input/output-only (ADR-006) — no facade/HttpClient/storage access. |
guards/ |
Route guards. | Kept separate from core/admin-auth/ because admin-auth.guard.ts is domain-specific; generic guards live here. |
Worked example, end to end: the Sprint 19 admin dashboard
src/app/features/admin/dashboard/ — read in the order a new engineer would build it.
-
Model —
models/admin-dashboard.model.ts. Plain interfaces/types for card data, card status (loading|empty|error|pending-backend|ready), health-check entries. No behavior, no imports from Angular DI. -
Gateway interface —
services/admin-dashboard-metrics.gateway.interface.ts. An abstract contract (AdminDashboardMetricsGateway) for "however we get category/product counts" — deliberately decoupled from how (local computation vs. real API) so the facade never knows which implementation is active. -
Gateway implementation —
services/admin-dashboard-metrics.local.gateway.ts.AdminDashboardMetricsLocalGateway implements AdminDashboardMetricsGateway, composingBackofficeDataService.loadCategories()/loadProducts()(already used elsewhere) into counts. A futureAdminDashboardMetricsApiGatewaywould implement the same interface against a real endpoint (docs/BACKEND.mditem 8) — nothing above this layer changes when that happens. -
DI token —
services/admin-dashboard-metrics-gateway.token.ts.const ADMIN_DASHBOARD_METRICS_GATEWAY = new InjectionToken<AdminDashboardMetricsGateway>(...), bound to the local gateway by default inapp.config.ts. This is the swap point: rebinding this token to a real API gateway is the only change needed to go from mock to real data. -
Supporting service —
services/admin-dashboard-history.service.ts.localStorage-backed activity log, scoped per tenant — a second, narrower concern (recent activity) that doesn't belong in the metrics gateway. -
Facade —
facade/admin-dashboard.facade.ts.AdminDashboardFacadeis the only thing the components below are allowed to inject. It composesProjectEditorFacade(existing — bootstrap/status/validation),ADMIN_DASHBOARD_METRICS_GATEWAY(via the token, not the concrete class), andAdminDashboardHistoryService, and exposes computed signals per card (status + value) plus the health-check list and quick-actions list. -
Presentational components —
components/admin-dashboard-card.component.*,admin-dashboard-quick-actions.component.*,admin-dashboard-activity.component.*,admin-dashboard-health.component.*. Each takes only@Input()s (card data, health entries, quick-action list) — noHttpClient, nolocalStorage, no route access, no facade injection. This is what makes them independently testable and reusable. -
Page container —
pages/admin-dashboard-page.component.*. InjectsAdminDashboardFacade, computes per-card status from bootstrap-loaded/metrics-error/empty conditions, prefixesrouterLinks with the current locale (LanguageService.currentLanguage()), and passes plain data down to the presentational components above. This is the only place in the feature that knows about routing or the facade. -
Route wiring —
app.routes.ts./:lang/backoffice/dashboard -> AdminDashboardPageComponent, guarded byadminAuthGuard;/:lang/backoffice(empty path) redirects todashboard.
Full narrative and known gaps: docs/ADMIN.md.
Steps to add a new feature (derived from the example above)
- Decide: does this belong in
features/<area>/<feature>/, or is it simple enough forpages/? Route-guarded, multi-component admin/backoffice work goes infeatures/admin/*orfeatures/backoffice/*. - Define the domain model(s) first (
models/*.model.ts) — no behavior, no DI. - If the feature needs data that might later come from a real backend, define a gateway/repository interface before writing any implementation.
- Implement a local/mock gateway against existing data sources where possible (reuse, don't duplicate — check
core/*and other features' services first). - Create an
InjectionTokenfor the gateway and bind it to the local implementation inapp.config.ts(or the relevant provider scope). This is the seam a backend integration will use later — never inject the concrete class directly from a facade or component. - Write the facade. It is the only consumer of the gateway token, and the only thing components inject.
- Build presentational components as
@Input()/@Output()-only — verify none of them importHttpClient, storage, or a facade. - Build the container/page component that injects the facade and wires routing.
- Add routes in
app.routes.ts, withadminAuthGuard(or the relevant guard) if it's an admin surface. - Add every new user-facing string to
i18n/translations.ts(interface) thenen.ts/ru.ts/hy.ts— never hardcode copy in a template. - Document backend gaps (if any) in
docs/BACKEND.mdusing the same "current behavior / gap / endpoint needed / files that change" structure as the existing entries. - Run
npm run arch:check(import boundaries + circular dependencies) andnpx tsc -p tsconfig.app.json --noEmitbefore committing.