Skip to content

IDTS-LAB/angular-codebase

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

11 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

SaaS Business App

A production-ready enterprise SaaS application built with Angular 22.0.7, combining Domain-Driven Design (DDD), Vertical Slice Architecture, Clean Architecture, and Atomic Design UI methodology. State management via NgRx SignalStore with Angular Signals, styled with Tailwind CSS v4 and SCSS.

Tech Stack

Category Technology
Framework Angular 22.0.7 (Standalone Components, Signals, esbuild)
Language TypeScript 6.0 (strict)
State Management NgRx SignalStore 21.1.1 (signals), RxJS (HTTP streams only)
UI Atomic Design (atoms/molecules/organisms), Angular Material 22
Styling Tailwind CSS v4, SCSS, CSS Custom Properties (design tokens)
Build @angular/build:application (esbuild, PostCSS)
Testing Vitest (unit), Playwright (E2E)
Linting ESLint flat config, Prettier
Git Hooks Husky, commitlint, lint-staged
CI/CD GitHub Actions, Docker (multi-stage, Nginx)
Runtime Node 24.18.0, Nginx

Architecture

Layered Architecture (per feature)

src/
├── app/
│   ├── core/              # Cross-cutting services (theme, navigation)
│   ├── domain/            # Shared domain primitives (BaseEntity, ValueObject, enums)
│   ├── infrastructure/    # Shared infrastructure (HTTP, storage, logging)
│   ├── config/            # Environment config, feature flags
│   ├── layout/            # App shells (public, private), sidebar, topbar
│   ├── ui/                # Atomic Design system (atoms → molecules → organisms)
│   └── features/          # Vertical slices
│       └── <feature>/
│           ├── domain/         # Entities, repository interfaces
│           │   ├── entities/
│           │   └── repository/
│           ├── application/    # Use cases, facades/stores, DTOs, mappers
│           │   ├── facade/
│           │   ├── dto/
│           │   └── mappers/
│           ├── infrastructure/ # API implementations
│           │   └── http/
│           └── presentation/   # Pages, components
│               └── pages/

Each feature is a self-contained vertical slice with its own domain, application, infrastructure, and presentation layers. Features communicate only through shared domain entities or the store.

State Management Pattern

  • Global/feature state: NgRx SignalStore with rxMethod for async operations
  • Local/UI state: Angular Signals (signal(), computed())
  • HTTP: RxJS Observable streams → wrapped in Result<T> discriminated union

SignalStores follow a consistent pattern:

signalStore(
  { providedIn: 'root' },
  withState(initialState),
  withMethods((store, api = inject(ApiService)) => ({
    load: rxMethod<void>(pipe(
      tap(() => patchState(store, { isLoading: true })),
      switchMap(() => api.get().pipe(tap(), catchError())),
    )),
  })),
)

Note: rxMethod methods and regular methods that call store.method() must live in separate withMethods blocks due to type inference constraints.

API Layer

All HTTP calls go through ApiService which wraps responses in a Result<T> discriminated union:

type Result<T> = Success<T> | Failure;
interface Success<T> {
  data: T;
  isSuccess: true;
  error: null;
}
interface Failure {
  data: null;
  isSuccess: false;
  error: Error;
}

Repository implementations map from API DTOs to domain entities via injectable Mapper classes. The API is mock-ready — each feature's repository interface can be swapped with a mock implementation.

UI Architecture (Atomic Design)

ui/
├── atoms/          # Single-responsibility components
│   ├── button.component.ts
│   ├── input.component.ts (ControlValueAccessor)
│   ├── badge.component.ts
│   ├── avatar.component.ts
│   └── spinner.component.ts
├── molecules/      # Composite components (2-3 atoms)
│   ├── card.component.ts
│   └── toast.component.ts
└── organisms/      # Complex, domain-agnostic widgets
    ├── data-table.component.ts
    ├── dialog.component.ts
    ├── form-field.component.ts
    ├── empty-state.component.ts
    └── error-state.component.ts

Design tokens are defined as CSS custom properties in styles.scss and shared across all components.

Features

All routes are lazy-loaded with standalone components.

Route Feature Status
/auth/login Authentication Done
/auth/register Registration Done
/app/dashboard Dashboard Done
/app/users User CRUD Done
/app/products Products Done
/app/orders Orders Done
/app/notifications Notifications Done
/app/profile Profile Done
/403 Forbidden Done
/** Not Found Done

Authentication

  • JWT + refresh token flow
  • AuthStore (SignalStore) manages session state
  • AuthInterceptor attaches JWT from StorageService
  • AuthGuard protects private routes

User Management

  • Full CRUD with pagination, search, DataTable
  • UsersStore with load, setPage, setSearch, deleteUser
  • User list with sortable columns and pagination controls

Dashboard

  • Stats cards (total users, revenue, orders, growth, etc.)
  • Loading skeleton states and error handling
  • DashboardStore with single load rxMethod

Notifications & Profile

  • Notification list with unread indicators and mark-as-read
  • Profile edit form with save

Products & Orders

  • List views with DataTable and pagination
  • Products: pageable with search
  • Orders: pageable list

Getting Started

Prerequisites

  • Node.js >= 22.18.0 (v22+ required for Angular 22)
  • npm >= 11
  • Angular CLI (optional, npx ng works)

Installation

npm ci

Development Server

npm start
# or
ng serve

Navigate to http://localhost:4200/. The app auto-reloads on source changes.

Scripts

Command Description
npm start Start dev server
npm run build Production build
npm run watch Build in watch mode (dev)
npm test Run unit tests (Vitest)
npm run lint ESLint check
npm run format Format with Prettier
npm run format:check Check Prettier formatting

Building for Production

npm run build

Build artifacts go to dist/saas-business-app/browser/. The build uses esbuild for fast bundling and tree-shaking.

Docker

Build Image

docker build -t saas-business-app .

Run Container

docker run -p 80:80 saas-business-app

Docker Compose

docker compose up

The Dockerfile uses a multi-stage build:

  1. Build stage: node:24-alpine — installs deps, runs production build
  2. Serve stage: nginx:alpine — serves the built artifacts with Nginx

Nginx is configured with SPA fallback (try_files $uri /index.html) and an /api/ reverse proxy.

CI/CD (GitHub Actions)

Push to main or open a PR to trigger:

  1. Lint — ESLint on the full codebase
  2. Build — Production build, artifacts uploaded

Workflow: .github/workflows/ci.yml

Project Structure (full)

.
├── .dockerignore
├── .github/
│   └── workflows/
│       └── ci.yml                 # CI pipeline
├── .husky/
│   ├── commit-msg                  # commitlint
│   └── pre-commit                  # lint-staged
├── .postcssrc.json                 # PostCSS with @tailwindcss/postcss
├── .prettierrc                     # Prettier config
├── Dockerfile                      # Multi-stage Docker build
├── commitlint.config.js
├── docker-compose.yml
├── eslint.config.js                # ESLint flat config
├── nginx.conf                      # Nginx SPA config
├── package.json
├── tsconfig.json                   # Strict TS, path aliases
└── src/
    └── app/
        ├── app.component.ts
        ├── app.config.ts           # DI providers (interceptors, configs, tokens)
        ├── app.routes.ts           # Lazy-loaded route tree
        ├── core/
        │   ├── services/
        │   │   ├── navigation.service.ts
        │   │   └── theme.service.ts
        │   ├── interceptors/
        │   │   ├── auth.interceptor.ts
        │   │   ├── error.interceptor.ts
        │   │   └── logging.interceptor.ts
        │   └── guards/
        │       └── auth.guard.ts
        ├── domain/
        │   ├── entities/
        │   │   └── base.entity.ts
        │   ├── enums/
        │   │   ├── permission.enum.ts
        │   │   ├── role.enum.ts
        │   │   ├── status.enum.ts
        │   │   └── theme.enum.ts
        │   └── exceptions/
        │       ├── domain.exception.ts
        │       ├── not-found.exception.ts
        │       ├── unauthorized.exception.ts
        │       └── validation.exception.ts
        ├── infrastructure/
        │   ├── http/
        │   │   ├── api.service.ts          # Base HTTP with Result<T>
        │   │   ├── api-config.ts           # API_CONFIG token
        │   │   └── result.ts               # Result discriminated union
        │   ├── logger/
        │   │   └── logger.service.ts       # LOGGER token + console impl
        │   └── storage/
        │       └── storage.service.ts      # localStorage/ssr-safe storage
        ├── config/
        │   ├── environment.ts
        │   └── feature-flags.ts
        ├── layout/
        │   ├── pages/
        │   │   ├── forbidden/
        │   │   │   └── forbidden.component.ts
        │   │   └── not-found/
        │   │       └── not-found.component.ts
        │   ├── private-layout/
        │   │   ├── private-layout.component.ts
        │   │   ├── sidebar/
        │   │   │   └── sidebar.component.ts
        │   │   └── topbar/
        │   │       └── topbar.component.ts
        │   ├── public-layout/
        │   │   └── public-layout.component.ts
        │   └── breadcrumb/
        │       └── breadcrumb.component.ts
        ├── ui/
        │   ├── atoms/
        │   │   ├── avatar/
        │   │   │   └── avatar.component.ts
        │   │   ├── badge/
        │   │   │   └── badge.component.ts
        │   │   ├── button/
        │   │   │   └── button.component.ts
        │   │   ├── input/
        │   │   │   └── input.component.ts
        │   │   └── spinner/
        │   │       └── spinner.component.ts
        │   ├── molecules/
        │   │   ├── card/
        │   │   │   └── card.component.ts
        │   │   └── toast/
        │   │       └── toast.component.ts
        │   └── organisms/
        │       ├── data-table/
        │       │   └── data-table.component.ts
        │       ├── dialog/
        │       │   └── dialog.component.ts
        │       ├── empty-state/
        │       │   └── empty-state.component.ts
        │       ├── error-state/
        │       │   └── error-state.component.ts
        │       └── form-field/
        │           └── form-field.component.ts
        └── features/
            ├── authentication/
            │   ├── domain/
            │   │   ├── entities/
            │   │   │   ├── session.entity.ts
            │   │   │   └── user.entity.ts
            │   │   └── repository/
            │   │       └── iauth.repository.ts
            │   ├── application/
            │   │   ├── dto/
            │   │   │   ├── login-request.dto.ts
            │   │   │   ├── login-response.dto.ts
            │   │   │   └── register-request.dto.ts
            │   │   ├── mappers/
            │   │   │   └── auth.mapper.ts
            │   │   └── facade/
            │   │       └── auth.store.ts
            │   ├── infrastructure/
            │   │   └── http/
            │   │       └── auth-api.service.ts
            │   └── presentation/
            │       └── pages/
            │           ├── login/
            │           │   └── login.page.ts
            │           └── register/
            │               └── register.page.ts
            ├── dashboard/
            │   ├── domain/
            │   │   ├── entities/
            │   │   │   └── dashboard-stats.entity.ts
            │   │   └── repository/
            │   │       └── idashboard.repository.ts
            │   ├── application/
            │   │   └── facade/
            │   │       └── dashboard.store.ts
            │   ├── infrastructure/
            │   │   └── http/
            │   │       └── dashboard-api.service.ts
            │   └── presentation/
            │       └── pages/
            │           └── dashboard/
            │               └── dashboard.page.ts
            ├── users/
            │   ├── domain/
            │   │   ├── entities/
            │   │   │   └── user.entity.ts
            │   │   └── repository/
            │   │       └── iuser.repository.ts
            │   ├── application/
            │   │   ├── dto/
            │   │   │   └── user.dto.ts
            │   │   ├── mappers/
            │   │   │   └── user.mapper.ts
            │   │   └── facade/
            │   │       └── users.store.ts
            │   ├── infrastructure/
            │   │   └── http/
            │   │       └── users-api.service.ts
            │   └── presentation/
            │       └── pages/
            │           ├── user-form/
            │           │   └── user-form.page.ts
            │           └── user-list/
            │               └── user-list.page.ts
            ├── products/
            │   ├── domain/
            │   │   ├── entities/
            │   │   │   └── product.entity.ts
            │   │   └── repository/
            │   │       └── iproduct.repository.ts
            │   ├── application/
            │   │   └── facade/
            │   │       └── products.store.ts
            │   ├── infrastructure/
            │   │   └── http/
            │   │       └── products-api.service.ts
            │   └── presentation/
            │       └── pages/
            │           └── product-list/
            │               └── product-list.page.ts
            ├── orders/
            │   ├── domain/
            │   │   ├── entities/
            │   │   │   └── order.entity.ts
            │   │   └── repository/
            │   │       └── iorder.repository.ts
            │   ├── application/
            │   │   └── facade/
            │   │       └── orders.store.ts
            │   ├── infrastructure/
            │   │   └── http/
            │   │       └── orders-api.service.ts
            │   └── presentation/
            │       └── pages/
            │           └── order-list/
            │               └── order-list.page.ts
            ├── notifications/
            │   ├── domain/
            │   │   ├── entities/
            │   │   │   └── notification.entity.ts
            │   │   └── repository/
            │   │       └── inotification.repository.ts
            │   ├── application/
            │   │   └── facade/
            │   │       └── notifications.store.ts
            │   ├── infrastructure/
            │   │   └── http/
            │   │       └── notifications-api.service.ts
            │   └── presentation/
            │       └── pages/
            │           └── notifications/
            │               └── notifications.page.ts
            └── profile/
                ├── domain/
                │   ├── entities/
                │   │   └── profile.entity.ts
                │   └── repository/
                │       └── iprofile.repository.ts
                ├── application/
                │   └── facade/
                │       └── profile.store.ts
                ├── infrastructure/
                │   └── http/
                │       └── profile-api.service.ts
                └── presentation/
                    └── pages/
                        └── profile/
                            └── profile.page.ts

About

No description, website, or topics provided.

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors