A directory reference for the SaaS Business App frontend. Each entry has a one-line responsibility. Paths reflect the actual codebase; where the in-repo README differs, this file is authoritative.
.
├── .github/workflows/ci.yml # CI pipeline: lint, test, build jobs (Node 24)
├── .husky/
│ ├── pre-commit # Runs lint-staged (prettier + eslint --fix)
│ └── commit-msg # Runs commitlint on the commit message
├── .postcssrc.json # PostCSS config: @tailwindcss/postcss plugin
├── .prettierrc # Prettier: printWidth 100, singleQuote, angular html parser
├── .vscode/ # Editor recommendations (optional)
├── .editorconfig # Editor indentation / EOL conventions
├── .dockerignore / .gitignore # Build/runtime ignore rules
├── ANGULAR_FRONTEND_CODEBASE_PROMPT.md # Original generation spec / requirements
├── ARCHITECTURE.md # Architecture document (this set)
├── CODING_STANDARDS.md # Coding conventions
├── CONTRIBUTING.md # Contribution guide
├── FOLDER_STRUCTURE.md # This file
├── Dockerfile # Multi-stage build: node:24-alpine -> nginx:alpine
├── docker-compose.yml # app (nginx) + backend (placeholder) services
├── nginx.conf # Nginx SPA + /api proxy + security headers + gzip + cache
├── commitlint.config.js # Conventional Commits type-enum + config-conventional
├── eslint.config.js # ESLint flat config (typescript-eslint + prettier)
├── package.json # Scripts, deps, lint-staged, packageManager
├── package-lock.json # Lockfile (npm ci source of truth)
├── tsconfig.json # Base TS config: strict, path aliases, angular compiler opts
├── tsconfig.app.json # App build config (excludes specs)
├── tsconfig.spec.json # Test config (vitest/globals, includes *.spec.ts)
├── angular.json # Angular workspace: build/serve/test targets, budgets
├── public/ # Static assets copied as-is (favicon.ico, mockServiceWorker.js)
├── docs/
│ └── adr/ # Architecture Decision Records
└── src/ # Application source (see section 2)
src/
├── main.ts # Bootstrap: starts MSW worker, bootstrapApplication(App)
├── index.html # App shell HTML
├── tailwind.css # @import 'tailwindcss'; (Tailwind v4 entry)
├── styles.scss # Global styles + design tokens (:root, [data-theme='dark'])
├── environments/
│ ├── environment.ts # Dev: apiBaseUrl http://localhost:3000/api/v1
│ ├── environment.prod.ts # Prod: apiBaseUrl /api/v1 (nginx-proxied)
│ └── environment.staging.ts # Staging environment overrides
├── mocks/
│ ├── worker.ts # MSW setup worker (browser)
│ └── handlers.ts # MSW request handlers (mock API)
└── app/
├── app.ts # Root component (selector app-root, app.html/app.scss)
├── app.html # Root template (<router-outlet>)
├── app.scss # Root styles
├── app.config.ts # ApplicationConfig: providers, interceptors, initializer, tokens
├── app.routes.ts # Lazy-loaded route tree (public/private shells, error pages)
├── core/ # Cross-cutting services, interceptors, tokens (feature-agnostic)
├── domain/ # Shared domain primitives (entities, enums, exceptions)
├── infrastructure/ # Shared infrastructure (HTTP, logger, storage)
├── config/ # App configuration (feature flags)
├── layout/ # App shells (public/private), shared chrome, error pages
├── ui/ # Atomic Design system (atoms / molecules / organisms)
└── features/ # Vertical feature slices (see section 3)
Every feature under src/app/features/<feature>/ follows the same shape. Reference:
authentication, users, dashboard, products, orders, notifications, profile.
features/<feature>/
├── domain/
│ ├── entities/ # Domain entities (pure TS, extend BaseEntity where applicable)
│ └── repository/ # IXRepository interface(s) + shared types (e.g. PaginatedResponse)
├── application/
│ ├── dto/ # Request/response DTOs (snake_case wire shapes)
│ ├── mappers/ # XMapper: DTO <-> Entity (injectable)
│ └── facade/ # XStore: SignalStore (withState/withComputed/withMethods/rxMethod)
├── infrastructure/
│ └── http/ # XApiService implements IXRepository; interceptors (auth only)
├── presentation/
│ ├── pages/ # Page components (XPage), each a lazy-loaded route target
│ └── guards/ # Route guards (auth.guard.ts; CanMatchFn)
└── <feature>.routes.ts # Optional feature sub-routes (default export), e.g. users.routes.ts
Concrete example (features/authentication/):
authentication/
├── domain/
│ ├── entities/ session.entity.ts, user.entity.ts, auth-token.entity.ts
│ └── repository/ iauth.repository.ts (IAuthRepository)
├── application/
│ ├── dto/ login-request.dto.ts, login-response.dto.ts, register-request.dto.ts
│ ├── mappers/ auth.mapper.ts (AuthMapper)
│ └── facade/ auth.store.ts (AuthStore)
├── infrastructure/
│ └── http/ auth-api.service.ts, auth-refresh.service.ts, auth.interceptor.ts
└── presentation/
├── pages/ login/, register/, forgot-password/
└── guards/ auth.guard.ts
Note: the authentication feature additionally owns a infrastructure/storage/ folder
(auth-storage.ts token, local-storage-auth-storage.ts, cookie-auth-storage.ts)
because token storage is auth-specific. Other features do not need it.
src/app/core/
├── index.ts # Barrel: APP_NAME/VERSION/API_BASE_URL, ThemeService, NavigationService
├── error/global-error.handler.ts # GlobalErrorHandler -> /error page (registered as ErrorHandler)
├── interceptors/
│ ├── error.interceptor.ts # 403 -> /403, logs HTTP errors
│ ├── logging.interceptor.ts # Per-request perf logging via LOGGER
│ └── index.ts # Barrel (re-exports errorInterceptor, loggingInterceptor)
├── pipes/safe-html.pipe.ts # Sanitizes HTML for safe binding
├── services/
│ ├── theme.service.ts # Signal-based light/dark theme (data-theme attribute)
│ └── navigation.service.ts # Navigation/breadcrumb state
└── tokens/app.tokens.ts # APP_NAME, APP_VERSION, API_BASE_URL injection tokens
The authentication interceptor (
authInterceptor) andauthGuardlive inside thefeatures/authentication/slice, not incore/.core/interceptors/holds only the feature-agnostic interceptors.
src/app/domain/
├── index.ts # Barrel
├── entities/ # BaseEntity, AuditableEntity, ValueObject (pure TS)
├── enums/ # Role, Status, Theme, Permission enums
└── exceptions/ # Domain exception types (domain.exception.ts)
src/app/infrastructure/
├── http/
│ ├── api.service.ts # ApiService: wraps HttpClient -> Observable<Result<T>>
│ ├── api-config.ts # API_CONFIG token + defaultApiConfig (baseUrl/timeout/retry)
│ └── result.ts # Result<T> = Success<T> | Failure (+ success/failure helpers)
├── logger/logger.service.ts # LOGGER token + ConsoleLoggerService (debug/info/warn/error/perf)
└── storage/storage.service.ts # SSR-safe localStorage JSON wrapper
src/app/config/
├── index.ts # Barrel
└── feature-flags.ts # FeatureFlags interface + defaultFeatureFlags
src/app/layout/
├── components/
│ ├── sidebar/sidebar.component.ts # App sidebar navigation
│ ├── topbar/topbar.component.ts # App top bar (menu toggle, theme, profile)
│ └── breadcrumb/breadcrumb.component.ts # Breadcrumb trail
├── pages/
│ ├── forbidden/forbidden.component.ts # /403 page
│ ├── error/error.component.ts # /error page (target of GlobalErrorHandler)
│ └── not-found/not-found.component.ts # /** page
├── private-layout/private-layout.component.ts # Shell for authenticated /app routes
└── public-layout/public-layout.component.ts # Shell for unauthenticated routes (login/register)
src/app/ui/
├── atoms/ # Single-responsibility primitives
│ ├── button/button.component.ts # Variants/size via host-context; aria-busy/disabled
│ ├── input/input.component.ts # ControlValueAccessor; input() + model()
│ ├── badge/badge.component.ts
│ ├── avatar/avatar.component.ts
│ ├── spinner/spinner.component.ts
│ └── index.ts
├── molecules/ # Composite components (2-3 atoms)
│ ├── card/card.component.ts
│ ├── toast/toast.component.ts
│ └── index.ts
└── organisms/ # Complex, domain-agnostic widgets
├── data-table/data-table.component.ts
├── dialog/dialog.component.ts
├── form-field/form-field.component.ts
├── empty-state/empty-state.component.ts
├── error-state/error-state.component.ts
└── index.ts
All UI components are standalone, OnPush, and token-driven (see CODING_STANDARDS.md
section 6-7).
| File | Purpose |
|---|---|
angular.json |
Workspace + saas-business-app project; @angular/build:application builder; production budgets (initial 500 kB warn / 1 MB error; component style 4 kB warn / 8 kB error); outputHashing: all; styles tailwind.css + styles.scss; assets from public/ |
tsconfig.json |
Strict TS, noImplicitReturns, noUnusedLocals/Parameters, noPropertyAccessFromIndexSignature, target ES2022, module: preserve; path aliases @core/@domain/@infrastructure/@features/@ui/@layout/@config/@env/@shared; Angular strictInjectionParameters/strictInputAccessModifiers/strictTemplates |
tsconfig.app.json |
App build: includes src/**/*.ts, excludes specs |
tsconfig.spec.json |
Test build: types: ["vitest/globals"], includes src/**/*.spec.ts |
eslint.config.js |
Flat config; typescript-eslint recommended; no-explicit-any: error; prettier integration; spec overrides |
.prettierrc |
printWidth: 100, singleQuote: true, angular parser for *.html |
.postcssrc.json |
@tailwindcss/postcss plugin (Tailwind v4) |
commitlint.config.js |
@commitlint/config-conventional + type-enum |
package.json |
lint-staged config (prettier + eslint --fix), packageManager: npm@11.16.0 |
| You are adding... | Location | Notes |
|---|---|---|
| A new business capability | src/app/features/<feature>/ |
Create the four layers (domain/application/infrastructure/presentation); add routes to app.routes.ts or a feature <feature>.routes.ts |
| A new domain entity | features/<f>/domain/entities/ (feature-specific) or src/app/domain/entities/ (shared) |
Pure TS, interface, extend BaseEntity if auditable |
| A new repository | features/<f>/domain/repository/ix.repository.ts (interface) + features/<f>/infrastructure/http/x-api.service.ts (impl) + features/<f>/application/mappers/x.mapper.ts |
|
| A new store | features/<f>/application/facade/<f>.store.ts |
signalStore({ providedIn: 'root' }, ...); split withMethods if methods call each other |
| A new page | features/<f>/presentation/pages/<page>/<page>.page.ts |
Standalone, OnPush; class XPage; lazy-load from routes |
| A new UI primitive | src/app/ui/atoms/<name>/<name>.component.ts (single concern) |
Standalone, OnPush, signal inputs, tokens, ARIA; export from atoms/index.ts |
| A new composite UI | src/app/ui/molecules/... (2-3 atoms) or src/app/ui/organisms/... (complex widget) |
Same rules; export from the layer's index.ts |
| A new HTTP interceptor | src/app/core/interceptors/ (feature-agnostic) or features/authentication/infrastructure/http/ (auth-specific) |
HttpInterceptorFn; register in app.config.ts withInterceptors([...]); export from core/interceptors/index.ts if in core |
| A new guard | features/<f>/presentation/guards/<name>.guard.ts |
CanMatchFn / CanActivateFn; attach via canMatch/canActivate on the route |
| A new shared service | src/app/core/services/ (cross-cutting) or inside the feature (feature-specific) |
providedIn: 'root'; export from core/index.ts if shared |
| A new injection token | src/app/core/tokens/ (cross-cutting) or colocated with its consumer (feature-specific) |
|
| A new enum | src/app/domain/enums/ (shared) |
|
| A new design token | src/styles.scss (:root + [data-theme='dark']) |
Never hardcode the value in a component |
| A new environment | src/environments/environment.<env>.ts |
Bind apiBaseUrl/appName/appVersion |
| A new ADR | docs/adr/NNNN-title.md |
Increment the number; follow the ADR template |