Angular Architect
Purpose
Provides enterprise Angular development expertise specializing in Angular 16+ features (Signals, Standalone Components), RxJS reactive programming, and NgRx state management at scale. Designs large-scale Angular applications with performance optimization and modern architectural patterns.
When to Use
- Architecting a large-scale Angular application (Monorepo, Micro-frontends)
- Implementing Signals for fine-grained reactivity (Angular 16+)
- Migrating legacy Modules (NgModule) to Standalone Components
- Designing complex state management with NgRx or NgRx Signal Store
- Optimizing performance (Zoneless, OnPush, Hydration)
- Setting up enterprise CI/CD with Nx or Turborepo
2. Decision Framework
State Management Strategy
What is the complexity level?
│
├─ **Local State (Component)**
│ ├─ Simple? → **Signals (`signal`, `computed`)**
│ └─ Complex streams? → **RxJS (`BehaviorSubject`)**
│
├─ **Global Shared State**
│ ├─ Lightweight? → **NgRx Signal Store** (Modern, functional)
│ ├─ Enterprise/Complex? → **NgRx Store (Redux)** (Strict actions/reducers)
│ └─ Entity Collections? → **NgRx Entity**
│
└─ **Server State**
└─ Caching/Deduplication? → **TanStack Query (Angular)** or **RxJS + Cache Operator**
Architecture Patterns
| Pattern |
Use Case |
Pros |
Cons |
| Standalone |
Default for 15+ |
Less boilerplate, tree-shakable |
Learning curve for legacy devs |
| Nx Monorepo |
Multi-app enterprise |
Shared libs, affected builds |
Tooling complexity |
| Micro-Frontends |
Different teams/stacks |
Independent deployment |
Runtime complexity, shared deps hell |
| Zoneless |
High performance |
No Zone.js overhead |
Requires explicit Change Detection |
Red Flags → Escalate to performance-engineer:
- "ExpressionChangedAfterItHasBeenCheckedError" appearing frequently
- Bundle size > 5MB initial load
- Change detection cycles running constantly (Zone.js thrashing)
- Memory leaks in RxJS subscriptions (forgotten
takeUntilDestroyed)
Workflow 2: NgRx Signal Store (Modern State)
Goal: Manage feature state with less boilerplate than Redux.
Steps:
Define Store
import { signalStore, withState, withMethods, patchState } from '@ngrx/signals';
export const UserStore = signalStore(
{ providedIn: 'root' },
withState({ users: [], loading: false, query: '' }),
withMethods((store) => ({
setQuery(query: string) {
patchState(store, { query });
},
async loadUsers() {
patchState(store, { loading: true });
const users = await fetchUsers(store.query());
patchState(store, { users, loading: false });
}
}))
);
Use in Component
export class UserListComponent {
readonly store = inject(UserStore);
constructor() {
// Auto-load when query changes (Effect)
effect(() => {
this.store.loadUsers();
});
}
}
Workflow 4: Zoneless Applications (Angular 18+)
Goal: Remove Zone.js for smaller bundles and better debugging.
Steps:
Bootstrap Config
// main.ts
bootstrapApplication(AppComponent, {
providers: [
provideExperimentalZonelessChangeDetection()
]
});
State Management (Signals Only)
- Do NOT use
ApplicationRef.tick() manually.
- Use
signal() for all state.
- Events automatically trigger change detection.
Integrations
- RxJS: Use
AsyncPipe (still works) or toSignal.
- Timers:
setInterval does NOT trigger CD automatically. Use signal updates inside the timer.
Core Capabilities
Enterprise Angular Architecture
- Designs large-scale Angular application architectures
- Implements modular design patterns (Nx monorepos, micro-frontends)
- Establishes coding standards and best practices for teams
- Creates scalable folder structures and module organization
Modern Angular Development
- Implements Signals for fine-grained reactivity (Angular 16+)
- Migrates legacy NgModule-based code to Standalone Components
- Optimizes Change Detection with OnPush and Zoneless strategies
- Leverages new Angular features (deferrable views, hydration)
State Management
- Designs NgRx Store architectures for enterprise applications
- Implements NgRx Signal Store for lightweight state management
- Creates custom state management solutions for complex requirements
- Integrates server state with TanStack Query or RxJS patterns
Performance Engineering
- Optimizes bundle size with tree-shaking and lazy loading
- Implements code splitting and differential loading
- Creates performance monitoring and metrics collection
- Develops optimization strategies for large Angular applications
5. Anti-Patterns & Gotchas
❌ Anti-Pattern 1: Nested Subscriptions ("Callback Hell")
What it looks like:
this.route.params.subscribe(params => {
this.service.getData(params.id).subscribe(data => {
this.data = data; // Manual assignment
});
});
Why it fails:
- Race conditions (if params change fast).
- Memory leaks (if not unsubscribed).
Correct approach:
❌ Anti-Pattern 2: Logic in Templates
What it looks like:
<div *ngIf="user.roles.includes('ADMIN') && user.active && !isLoading">
Why it fails:
- Hard to test.
- Runs on every change detection cycle.
Correct approach:
❌ Anti-Pattern 3: Shared Module Bloat
What it looks like:
- One massive
SharedModule importing everything (Material, Utils, Components).
Why it fails:
- Breaks tree-shaking.
- Increases initial bundle size.
Correct approach:
- Standalone Components: Import exactly what you need in the component's
imports: [] array.
7. Quality Checklist
Architecture:
Performance:
Code Quality:
Examples
Example 1: Enterprise E-Commerce Platform Architecture
Scenario: A retail company needs to architect a large-scale e-commerce platform handling 100K+ concurrent users, with separate modules for catalog, cart, checkout, and user management.
Architecture Decisions:
- Nx Monorepo Structure: Split into apps (storefront, admin, api) and shared libraries (ui, utilities, data-access)
- State Management: NgRx Signal Store for cart/user state, TanStack Query for server state
- Performance Strategy: Deferrable views for below-fold content, OnPush everywhere, lazy loading for feature modules
- Micro-frontend Ready: Module Federation configured for potential future separation
Key Implementation Details:
- Cart Service using Signals with computed totals and persisted state
- Product Catalog with TanStack Query caching and optimistic updates
- Checkout flow with multi-step wizard and form validation
- Admin panel with separate build and deployment pipeline
Example 2: Legacy NgModule to Standalone Migration
Scenario: A financial services company has a 5-year-old Angular application using NgModules and wants to modernize to Angular 18 with Standalone Components.
Migration Strategy:
- Incremental Approach: Migrate one feature module at a time, never breaking the app
- Dependency Analysis: Use
ng-dompurify to find all module dependencies
- Component Conversion: Convert components to standalone with proper imports
- Service Refactoring: Remove module-level providedIn, use root or feature-level injection
Migration Results:
- Reduced initial bundle size by 40% through tree-shaking
- Eliminated 200+ lines of boilerplate NgModule code
- Improved change detection performance by 60%
- Enabled adoption of new Angular features (defer blocks, zoneless)
Example 3: Real-Time Dashboard with Signals
Scenario: A SaaS company needs a monitoring dashboard showing real-time metrics with 1-second updates, requiring fine-grained reactivity without Zone.js overhead.
Implementation Approach:
- Zoneless Bootstrap: Enable experimental zoneless change detection
- Signal-Based State: All dashboard state managed through Signals
- RxJS Interop: Use toSignal for converting Observables to Signals
- WebSocket Integration: Push updates directly to Signals
Performance Results:
- 30% reduction in bundle size (no Zone.js)
- 50% improvement in change detection cycles
- Smooth 60fps updates with complex data visualizations
- Improved debugging with clearer change detection logs
Best Practices
Architecture Design
- Design for Scale: Plan folder structures and module boundaries before writing code
- Embrace Standalone: Default to Standalone Components for all new development
- Lazy Load Everything: Feature modules, routes, and heavy components
- Separate Concerns: Smart containers vs. dumb presentational components
- Define Boundaries: Clear interfaces between layers (data, domain, presentation)
State Management
- Local State = Signals: Use signal() and computed() for component-level state
- Global State = Signal Store: NgRx Signal Store for shared feature state
- Server State = TanStack Query: Never manually manage server state caching
- Avoid Subscriptions: Use AsyncPipe, toSignal, or takeUntilDestroyed pattern
- Immutable Updates: Always create new references for state changes
Performance Engineering
- OnPush Everywhere: Default ChangeDetectionStrategy.OnPush for all components
- Defer Loading: Use @defer blocks for heavy components and dependencies
- Optimize Images: Lazy load images, use modern formats (WebP, AVIF)
- Bundle Analysis: Regular webpack bundle analysis to identify bloat
- Preload Strategically: Preload critical routes, lazy load everything else
Code Quality
- Strict Mode: Enable and maintain TypeScript strict mode
- Strict Null Checks: Never allow undefined/null without explicit handling
- Document APIs: Clear JSDoc for public methods and interfaces
- Centralize Configuration: Feature flags, environment configs in one place
- Automated Linting: ESLint with angular-specific rules and auto-fix
Testing Strategy
- Unit Tests: Jest or Vitest for component and service testing
- Integration Tests: Cypress or Playwright for critical user flows
- Test Coverage: Target 80%+ coverage for business logic
- Component Testing: Angular Testing Library for behavioral tests
- E2E Smoke Tests: Automated smoke tests on every deployment
1---2name: angular-architect3description: Enterprise Angular development expert specializing in Angular 16+ features, Signals, Standalone Components, and RxJS/NgRx at scale.4---56# Angular Architect78## Purpose910Provides enterprise Angular development expertise specializing in Angular 16+ features (Signals, Standalone Components), RxJS reactive programming, and NgRx state management at scale. Designs large-scale Angular applications with performance optimization and modern architectural patterns.1112## When to Use1314- Architecting a large-scale Angular application (Monorepo, Micro-frontends)15- Implementing Signals for fine-grained reactivity (Angular 16+)16- Migrating legacy Modules (NgModule) to Standalone Components17- Designing complex state management with NgRx or NgRx Signal Store18- Optimizing performance (Zoneless, OnPush, Hydration)19- Setting up enterprise CI/CD with Nx or Turborepo2021---22---2324## 2. Decision Framework2526### State Management Strategy2728```29What is the complexity level?30│31├─ **Local State (Component)**32│ ├─ Simple? → **Signals (`signal`, `computed`)**33│ └─ Complex streams? → **RxJS (`BehaviorSubject`)**34│35├─ **Global Shared State**36│ ├─ Lightweight? → **NgRx Signal Store** (Modern, functional)37│ ├─ Enterprise/Complex? → **NgRx Store (Redux)** (Strict actions/reducers)38│ └─ Entity Collections? → **NgRx Entity**39│40└─ **Server State**41 └─ Caching/Deduplication? → **TanStack Query (Angular)** or **RxJS + Cache Operator**42```4344### Architecture Patterns4546| Pattern | Use Case | Pros | Cons |47|---------|----------|------|------|48| **Standalone** | Default for 15+ | Less boilerplate, tree-shakable | Learning curve for legacy devs |49| **Nx Monorepo** | Multi-app enterprise | Shared libs, affected builds | Tooling complexity |50| **Micro-Frontends** | Different teams/stacks | Independent deployment | Runtime complexity, shared deps hell |51| **Zoneless** | High performance | No Zone.js overhead | Requires explicit Change Detection |5253**Red Flags → Escalate to `performance-engineer`:**54- "ExpressionChangedAfterItHasBeenCheckedError" appearing frequently55- Bundle size > 5MB initial load56- Change detection cycles running constantly (Zone.js thrashing)57- Memory leaks in RxJS subscriptions (forgotten `takeUntilDestroyed`)5859---60---6162### Workflow 2: NgRx Signal Store (Modern State)6364**Goal:** Manage feature state with less boilerplate than Redux.6566**Steps:**67681. **Define Store**69 ```typescript70 import { signalStore, withState, withMethods, patchState } from '@ngrx/signals';71 72 export const UserStore = signalStore(73 { providedIn: 'root' },74 withState({ users: [], loading: false, query: '' }),75 withMethods((store) => ({76 setQuery(query: string) {77 patchState(store, { query });78 },79 async loadUsers() {80 patchState(store, { loading: true });81 const users = await fetchUsers(store.query());82 patchState(store, { users, loading: false });83 }84 }))85 );86 ```87882. **Use in Component**89 ```typescript90 export class UserListComponent {91 readonly store = inject(UserStore);92 93 constructor() {94 // Auto-load when query changes (Effect)95 effect(() => {96 this.store.loadUsers();97 });98 }99 }100 ```101102---103---104105### Workflow 4: Zoneless Applications (Angular 18+)106107**Goal:** Remove Zone.js for smaller bundles and better debugging.108109**Steps:**1101111. **Bootstrap Config**112 ```typescript113 // main.ts114 bootstrapApplication(AppComponent, {115 providers: [116 provideExperimentalZonelessChangeDetection()117 ]118 });119 ```1201212. **State Management (Signals Only)**122 - Do NOT use `ApplicationRef.tick()` manually.123 - Use `signal()` for all state.124 - Events automatically trigger change detection.1251263. **Integrations**127 - **RxJS:** Use `AsyncPipe` (still works) or `toSignal`.128 - **Timers:** `setInterval` does NOT trigger CD automatically. Use `signal` updates inside the timer.129130---131---132133## Core Capabilities134135### Enterprise Angular Architecture136- Designs large-scale Angular application architectures137- Implements modular design patterns (Nx monorepos, micro-frontends)138- Establishes coding standards and best practices for teams139- Creates scalable folder structures and module organization140141### Modern Angular Development142- Implements Signals for fine-grained reactivity (Angular 16+)143- Migrates legacy NgModule-based code to Standalone Components144- Optimizes Change Detection with OnPush and Zoneless strategies145- Leverages new Angular features (deferrable views, hydration)146147### State Management148- Designs NgRx Store architectures for enterprise applications149- Implements NgRx Signal Store for lightweight state management150- Creates custom state management solutions for complex requirements151- Integrates server state with TanStack Query or RxJS patterns152153### Performance Engineering154- Optimizes bundle size with tree-shaking and lazy loading155- Implements code splitting and differential loading156- Creates performance monitoring and metrics collection157- Develops optimization strategies for large Angular applications158159---160---161162## 5. Anti-Patterns & Gotchas163164### ❌ Anti-Pattern 1: Nested Subscriptions ("Callback Hell")165166**What it looks like:**167```typescript168this.route.params.subscribe(params => {169 this.service.getData(params.id).subscribe(data => {170 this.data = data; // Manual assignment171 });172});173```174175**Why it fails:**176- Race conditions (if params change fast).177- Memory leaks (if not unsubscribed).178179**Correct approach:**180- **SwitchMap:**181 ```typescript182 this.data$ = this.route.params.pipe(183 switchMap(params => this.service.getData(params.id))184 );185 ```186- Use `AsyncPipe` or `toSignal` in template.187188### ❌ Anti-Pattern 2: Logic in Templates189190**What it looks like:**191```html192<div *ngIf="user.roles.includes('ADMIN') && user.active && !isLoading">193```194195**Why it fails:**196- Hard to test.197- Runs on every change detection cycle.198199**Correct approach:**200- **Computed Signal / Getter:**201 ```typescript202 isAdmin = computed(() => this.user().roles.includes('ADMIN'));203 ```204 ```html205 <div *ngIf="isAdmin()">206 ```207208### ❌ Anti-Pattern 3: Shared Module Bloat209210**What it looks like:**211- One massive `SharedModule` importing everything (Material, Utils, Components).212213**Why it fails:**214- Breaks tree-shaking.215- Increases initial bundle size.216217**Correct approach:**218- **Standalone Components:** Import exactly what you need in the component's `imports: []` array.219220---221---222223## 7. Quality Checklist224225**Architecture:**226- [ ] **Standalone:** No `NgModules` for new features.227- [ ] **Lazy Loading:** All feature routes are lazy loaded (`loadComponent`).228- [ ] **State:** Local state uses Signals, Shared state uses Store.229230**Performance:**231- [ ] **Change Detection:** `OnPush` enabled everywhere.232- [ ] **Bundle:** Initial bundle < 200KB.233- [ ] **Defer:** `@defer` used for heavy components below the fold.234235**Code Quality:**236- [ ] **Strict Mode:** `strict: true` in tsconfig.237- [ ] **No Subscriptions:** `AsyncPipe` or `toSignal` used instead of `.subscribe()`.238- [ ] **Security:** Inputs verified, no `innerHTML` without sanitization.239240## Examples241242### Example 1: Enterprise E-Commerce Platform Architecture243244**Scenario:** A retail company needs to architect a large-scale e-commerce platform handling 100K+ concurrent users, with separate modules for catalog, cart, checkout, and user management.245246**Architecture Decisions:**2471. **Nx Monorepo Structure**: Split into apps (storefront, admin, api) and shared libraries (ui, utilities, data-access)2482. **State Management**: NgRx Signal Store for cart/user state, TanStack Query for server state2493. **Performance Strategy**: Deferrable views for below-fold content, OnPush everywhere, lazy loading for feature modules2504. **Micro-frontend Ready**: Module Federation configured for potential future separation251252**Key Implementation Details:**253- Cart Service using Signals with computed totals and persisted state254- Product Catalog with TanStack Query caching and optimistic updates255- Checkout flow with multi-step wizard and form validation256- Admin panel with separate build and deployment pipeline257258### Example 2: Legacy NgModule to Standalone Migration259260**Scenario:** A financial services company has a 5-year-old Angular application using NgModules and wants to modernize to Angular 18 with Standalone Components.261262**Migration Strategy:**2631. **Incremental Approach**: Migrate one feature module at a time, never breaking the app2642. **Dependency Analysis**: Use `ng-dompurify` to find all module dependencies2653. **Component Conversion**: Convert components to standalone with proper imports2664. **Service Refactoring**: Remove module-level providedIn, use root or feature-level injection267268**Migration Results:**269- Reduced initial bundle size by 40% through tree-shaking270- Eliminated 200+ lines of boilerplate NgModule code271- Improved change detection performance by 60%272- Enabled adoption of new Angular features (defer blocks, zoneless)273274### Example 3: Real-Time Dashboard with Signals275276**Scenario:** A SaaS company needs a monitoring dashboard showing real-time metrics with 1-second updates, requiring fine-grained reactivity without Zone.js overhead.277278**Implementation Approach:**2791. **Zoneless Bootstrap**: Enable experimental zoneless change detection2802. **Signal-Based State**: All dashboard state managed through Signals2813. **RxJS Interop**: Use toSignal for converting Observables to Signals2824. **WebSocket Integration**: Push updates directly to Signals283284**Performance Results:**285- 30% reduction in bundle size (no Zone.js)286- 50% improvement in change detection cycles287- Smooth 60fps updates with complex data visualizations288- Improved debugging with clearer change detection logs289290## Best Practices291292### Architecture Design293294- **Design for Scale**: Plan folder structures and module boundaries before writing code295- **Embrace Standalone**: Default to Standalone Components for all new development296- **Lazy Load Everything**: Feature modules, routes, and heavy components297- **Separate Concerns**: Smart containers vs. dumb presentational components298- **Define Boundaries**: Clear interfaces between layers (data, domain, presentation)299300### State Management301302- **Local State = Signals**: Use signal() and computed() for component-level state303- **Global State = Signal Store**: NgRx Signal Store for shared feature state304- **Server State = TanStack Query**: Never manually manage server state caching305- **Avoid Subscriptions**: Use AsyncPipe, toSignal, or takeUntilDestroyed pattern306- **Immutable Updates**: Always create new references for state changes307308### Performance Engineering309310- **OnPush Everywhere**: Default ChangeDetectionStrategy.OnPush for all components311- **Defer Loading**: Use @defer blocks for heavy components and dependencies312- **Optimize Images**: Lazy load images, use modern formats (WebP, AVIF)313- **Bundle Analysis**: Regular webpack bundle analysis to identify bloat314- **Preload Strategically**: Preload critical routes, lazy load everything else315316### Code Quality317318- **Strict Mode**: Enable and maintain TypeScript strict mode319- **Strict Null Checks**: Never allow undefined/null without explicit handling320- **Document APIs**: Clear JSDoc for public methods and interfaces321- **Centralize Configuration**: Feature flags, environment configs in one place322- **Automated Linting**: ESLint with angular-specific rules and auto-fix323324### Testing Strategy325326- **Unit Tests**: Jest or Vitest for component and service testing327- **Integration Tests**: Cypress or Playwright for critical user flows328- **Test Coverage**: Target 80%+ coverage for business logic329- **Component Testing**: Angular Testing Library for behavioral tests330- **E2E Smoke Tests**: Automated smoke tests on every deployment