Step 1-2 (audit + plan): classified 35 project markdown files into Core/Architecture/ADR/Temporary-audit/Sprint-report/Generated-review/ Duplicate/Obsolete/Historical. Agent-tooling files (.agents/skills/**, .superpowers/**, docs/context/**, CLAUDE.md/GEMINI.md/AGENTS.md/ .github/copilot-instructions.md) explicitly out of scope — intentional per-tool duplication, not documentation debt. Step 3 (merge, no information lost): - docs/PROJECT.md -> docs/PROJECT_INDEX.md, rewritten as the single entry point: system overview, living-doc index, archive pointer, current status, and a critical-finding callout up top. - docs/backend/BACKEND-INTEGRATION.md -> docs/BACKEND_API.md, docs/backend/REMAINING-BACKEND-WORK.md -> docs/BACKEND_API_REMAINING_WORK.md (also folded in a legitimate uncommitted status update that had been sitting unstaged all session: categories marked DONE, order-creation endpoint noted done). - RELEASE-NOTES.md merged into CHANGELOG.md (was a near-duplicate of the same release content in friendlier prose), then deleted. - KNOWN-ISSUES.md: added item 13 (see below) and item 14 (missing canDeactivate on admin/products edit, from the archived PROJECT-STATE audit, re-verified still true); added a correction note to Fixed item 7. - All cross-references to renamed/moved files fixed across every kept doc (grep+sed pass, then verified with a link-existence check across all 58 in-scope markdown files -> 0 broken links). Step 4 (archive, nothing deleted without merging first): created docs/archive/, moved 19 files there (3 root sprint reports, 1 platform report, SPRINT-PLAN.md, and 14 one-off audit/review/report docs). Added correction headers to the 3 archived docs whose conclusions were affected by the finding below, rather than silently leaving them misleading. Step 5: docs/PROJECT_INDEX.md rewritten per the mission brief - someone opening the repo should understand the whole system from it. IMPORTANT FINDING (surfaced during this audit, not the mission's primary goal but too significant to bury): pages/category/*, pages/search/*, pages/item-detail/*, pages/info/**, pages/legal/** (40+ files) are entirely unrouted dead code - app.routes.ts's cmsContentRoutes is a literal empty array, and category/search/product routes redirect to CatalogContainerComponent/ ProductDetailsContainerComponent, not these files. Confirmed against app.routes.ts directly and cross-checked against FRONTEND.md's own routing description. This means several fixes from earlier this cycle (RC-Premium-01, RC STORE-01) and the dead-code cleanup sprint's conclusion that these files were live were all wrong - documented as KNOWN-ISSUES.md item 13, flagged at the top of PROJECT_INDEX.md, and noted on the 3 archived docs whose conclusions it affects. No application code was changed to fix this (out of scope per this session's 'documentation only' constraint) - it needs a wire-it-up-or- delete-it decision first. Verification: tsc --noEmit clean, npm run build green, all markdown links across 58 in-scope files resolve (checked programmatically). No application/Angular/backend code modified. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
8.0 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_API.md#615-backoffice--dashboard-metrics--recent-activity-planned) — 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_API.md(§6, endpoints by domain) using the same CURRENT/PLANNED/FUTURE tagging as the existing entries. - Run
npm run arch:check(import boundaries + circular dependencies) andnpx tsc -p tsconfig.app.json --noEmitbefore committing.