Skip to content

Latest commit

 

History

History
234 lines (209 loc) · 19.8 KB

File metadata and controls

234 lines (209 loc) · 19.8 KB

Folder Structure

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.

1. Repository root

.
├── .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)

2. src/ tree

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)

3. Feature internal structure (vertical slice)

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.

4. Shared layers (cross-feature)

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) and authGuard live inside the features/authentication/ slice, not in core/. 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)

5. Atomic Design UI structure

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).

6. Configuration files reference

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

7. Where to put new things

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