angular-architect
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseAngular Architect
Angular架构师
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.
提供企业级Angular开发专业支持,专注于Angular 16+特性(Signals、Standalone Components)、RxJS响应式编程以及大规模场景下的NgRx状态管理。设计具备性能优化和现代架构模式的大型Angular应用。
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
- 构建大型Angular应用架构(Monorepo、微前端)
- 实现基于Signals的细粒度响应式逻辑(Angular 16+)
- 将传统NgModule代码迁移至Standalone Components
- 基于NgRx或NgRx Signal Store设计复杂状态管理方案
- 优化应用性能(无Zone.js、OnPush变更检测、Hydration)
- 基于Nx或Turborepo搭建企业级CI/CD流程
2. Decision Framework
2. 决策框架
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**复杂度等级如何?
│
├─ **组件级本地状态**
│ ├─ 简单场景? → **Signals(`signal`、`computed`)**
│ └─ 复杂流场景? → **RxJS(`BehaviorSubject`)**
│
├─ **全局共享状态**
│ ├─ 轻量级场景? → **NgRx Signal Store**(现代、函数式)
│ ├─ 企业级/复杂场景? → **NgRx Store (Redux)**(严格的动作/ reducer模式)
│ └─ 实体集合场景? → **NgRx Entity**
│
└─ **服务端状态**
└─ 需要缓存/去重? → **TanStack Query (Angular)** 或 **RxJS + 缓存操作符**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
| 模式 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| Standalone | Angular 15+默认方案 | 更少样板代码、支持摇树优化 | 传统开发者存在学习曲线 |
| Nx Monorepo | 多应用企业级场景 | 共享库、增量构建 | 工具链复杂度较高 |
| 微前端 | 多团队/多技术栈场景 | 独立部署 | 运行时复杂度高、依赖共享问题 |
| 无Zone.js | 高性能需求场景 | 消除Zone.js性能开销 | 需要手动处理变更检测 |
红色预警 → 请联系:
performance-engineer- 频繁出现"ExpressionChangedAfterItHasBeenCheckedError"错误
- 初始加载包体积超过5MB
- 变更检测循环持续运行(Zone.js过度触发)
- RxJS订阅存在内存泄漏(未使用)
takeUntilDestroyed
Workflow 2: NgRx Signal Store (Modern State)
工作流2:NgRx Signal Store(现代状态管理)
Goal: Manage feature state with less boilerplate than Redux.
Steps:
-
Define Storetypescript
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 Componenttypescript
export class UserListComponent { readonly store = inject(UserStore); constructor() { // Auto-load when query changes (Effect) effect(() => { this.store.loadUsers(); }); } }
目标: 以比Redux更少的样板代码管理特性状态。
步骤:
-
定义Storetypescript
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 }); } })) ); -
在组件中使用typescript
export class UserListComponent { readonly store = inject(UserStore); constructor() { // 当query变化时自动加载(Effect) effect(() => { this.store.loadUsers(); }); } }
Workflow 4: Zoneless Applications (Angular 18+)
工作流4:无Zone.js应用(Angular 18+)
Goal: Remove Zone.js for smaller bundles and better debugging.
Steps:
-
Bootstrap Configtypescript
// main.ts bootstrapApplication(AppComponent, { providers: [ provideExperimentalZonelessChangeDetection() ] }); -
State Management (Signals Only)
- Do NOT use manually.
ApplicationRef.tick() - Use for all state.
signal() - Events automatically trigger change detection.
- Do NOT use
-
Integrations
- RxJS: Use (still works) or
AsyncPipe.toSignal - Timers: does NOT trigger CD automatically. Use
setIntervalupdates inside the timer.signal
- RxJS: Use
目标: 移除Zone.js以减小包体积并提升调试体验。
步骤:
-
启动配置typescript
// main.ts bootstrapApplication(AppComponent, { providers: [ provideExperimentalZonelessChangeDetection() ] }); -
状态管理(仅使用Signals)
- 不要手动调用。
ApplicationRef.tick() - 所有状态均使用管理。
signal() - 事件会自动触发变更检测。
- 不要手动调用
-
集成要点
- RxJS: 使用(仍可正常工作)或
AsyncPipe。toSignal - 定时器: 不会自动触发变更检测,需在定时器内部更新
setInterval。signal
- RxJS: 使用
Core Capabilities
核心能力
Enterprise Angular Architecture
企业级Angular架构
- 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
- 设计大型Angular应用架构
- 实现模块化设计模式(Nx monorepos、微前端)
- 为团队制定编码规范与最佳实践
- 创建可扩展的目录结构与模块组织方式
Modern Angular Development
现代Angular开发
- 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)
- 实现基于Signals的细粒度响应式逻辑(Angular 16+)
- 将传统NgModule代码迁移至Standalone Components
- 基于OnPush和无Zone.js策略优化变更检测
- 利用Angular新特性(延迟加载视图、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
- 为企业级应用设计NgRx Store架构
- 实现轻量级状态管理方案NgRx Signal Store
- 为复杂需求定制状态管理解决方案
- 集成服务端状态与TanStack Query或RxJS模式
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
- 通过摇树优化与懒加载减小包体积
- 实现代码分割与差异化加载
- 搭建性能监控与指标收集体系
- 为大型Angular应用制定优化策略
5. Anti-Patterns & Gotchas
5. 反模式与注意事项
❌ Anti-Pattern 1: Nested Subscriptions ("Callback Hell")
❌ 反模式1:嵌套订阅("回调地狱")
What it looks like:
typescript
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:
- SwitchMap:
typescript
this.data$ = this.route.params.pipe( switchMap(params => this.service.getData(params.id)) ); - Use or
AsyncPipein template.toSignal
表现:
typescript
this.route.params.subscribe(params => {
this.service.getData(params.id).subscribe(data => {
this.data = data; // 手动赋值
});
});问题:
- 竞态条件(参数快速变化时)。
- 内存泄漏(未取消订阅时)。
正确方案:
- SwitchMap:
typescript
this.data$ = this.route.params.pipe( switchMap(params => this.service.getData(params.id)) ); - 在模板中使用或
AsyncPipe。toSignal
❌ Anti-Pattern 2: Logic in Templates
❌ 反模式2:模板中包含业务逻辑
What it looks like:
html
<div *ngIf="user.roles.includes('ADMIN') && user.active && !isLoading">Why it fails:
- Hard to test.
- Runs on every change detection cycle.
Correct approach:
- Computed Signal / Getter:
typescript
isAdmin = computed(() => this.user().roles.includes('ADMIN'));html<div *ngIf="isAdmin()">
表现:
html
<div *ngIf="user.roles.includes('ADMIN') && user.active && !isLoading">问题:
- 难以测试。
- 每次变更检测循环都会执行。
正确方案:
- 计算Signal / Getter:
typescript
isAdmin = computed(() => this.user().roles.includes('ADMIN'));html<div *ngIf="isAdmin()">
❌ Anti-Pattern 3: Shared Module Bloat
❌ 反模式3:共享模块臃肿
What it looks like:
- One massive importing everything (Material, Utils, Components).
SharedModule
Why it fails:
- Breaks tree-shaking.
- Increases initial bundle size.
Correct approach:
- Standalone Components: Import exactly what you need in the component's array.
imports: []
表现:
- 单个庞大的导入所有内容(Material、工具类、组件)。
SharedModule
问题:
- 破坏摇树优化。
- 增加初始加载包体积。
正确方案:
- Standalone Components: 在组件的数组中仅导入所需内容。
imports: []
7. Quality Checklist
7. 质量检查清单
Architecture:
- Standalone: No for new features.
NgModules - Lazy Loading: All feature routes are lazy loaded ().
loadComponent - State: Local state uses Signals, Shared state uses Store.
Performance:
- Change Detection: enabled everywhere.
OnPush - Bundle: Initial bundle < 200KB.
- Defer: used for heavy components below the fold.
@defer
Code Quality:
- Strict Mode: in tsconfig.
strict: true - No Subscriptions: or
AsyncPipeused instead oftoSignal..subscribe() - Security: Inputs verified, no without sanitization.
innerHTML
架构:
- Standalone: 新特性不使用。
NgModules - 懒加载: 所有特性路由均启用懒加载()。
loadComponent - 状态管理: 本地状态使用Signals,共享状态使用Store。
性能:
- 变更检测: 全局启用。
OnPush - 包体积: 初始包体积小于200KB。
- 延迟加载: 对首屏以下的重型组件使用。
@defer
代码质量:
- 严格模式: tsconfig中启用。
strict: true - 无手动订阅: 使用或
AsyncPipe替代toSignal。.subscribe() - 安全性: 验证输入内容,不使用未清理的。
innerHTML
Examples
示例
Example 1: Enterprise E-Commerce Platform Architecture
示例1:企业级电商平台架构
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
场景: 某零售企业需要构建支持10万+并发用户的大型电商平台,包含商品目录、购物车、结算、用户管理等独立模块。
架构决策:
- Nx Monorepo结构:拆分为应用(前台、后台、API)和共享库(UI组件、工具类、数据访问)
- 状态管理:购物车/用户状态使用NgRx Signal Store,服务端状态使用TanStack Query
- 性能策略:首屏以下内容使用延迟加载视图,全局启用OnPush,特性模块懒加载
- 微前端就绪:配置Module Federation为未来拆分做准备
核心实现细节:
- 基于Signals的购物车服务,包含计算属性和持久化状态
- 基于TanStack Query缓存和乐观更新的商品目录
- 多步骤向导式结算流程与表单验证
- 独立构建和部署的后台管理面板
Example 2: Legacy NgModule to Standalone Migration
示例2:从传统NgModule迁移至Standalone Components
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 to find all module dependencies
ng-dompurify - 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)
场景: 某金融服务企业拥有一个5年历史的Angular应用,基于NgModule构建,希望升级到Angular 18并使用Standalone Components。
迁移策略:
- 增量迁移:逐个迁移特性模块,不中断现有应用运行
- 依赖分析:使用分析所有模块依赖
ng-dompurify - 组件转换:将组件转换为Standalone并配置正确的导入
- 服务重构:移除模块级providedIn,使用根注入或特性级注入
迁移成果:
- 通过摇树优化将初始包体积减少40%
- 消除200+行NgModule样板代码
- 变更检测性能提升60%
- 支持Angular新特性(延迟块、无Zone.js)
Example 3: Real-Time Dashboard with Signals
示例3:基于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
场景: 某SaaS企业需要开发实时监控面板,支持1秒级数据更新,要求细粒度响应式且无Zone.js性能开销。
实现方案:
- 无Zone.js启动:启用实验性无Zone.js变更检测
- Signal状态管理:所有面板状态均通过Signals管理
- RxJS互操作:使用toSignal将Observables转换为Signals
- WebSocket集成:直接将推送更新写入Signals
性能成果:
- 包体积减少30%(移除Zone.js)
- 变更检测循环效率提升50%
- 复杂数据可视化仍保持60fps流畅更新
- 变更检测日志更清晰,调试体验提升
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)
- 面向扩展设计:编写代码前规划目录结构与模块边界
- 优先使用Standalone:所有新开发默认使用Standalone Components
- 全量懒加载:特性模块、路由、重型组件均启用懒加载
- 关注点分离:智能容器组件与纯展示组件分离
- 明确边界:各层(数据、领域、展示)之间定义清晰接口
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
- 本地状态=Signals:组件级状态使用signal()和computed()
- 全局状态=Signal Store:共享特性状态使用NgRx Signal Store
- 服务端状态=TanStack Query:绝不手动管理服务端状态缓存
- 避免手动订阅:使用AsyncPipe、toSignal或takeUntilDestroyed模式
- 不可变更新:状态变更始终创建新引用
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
- 全局OnPush:所有组件默认启用ChangeDetectionStrategy.OnPush
- 延迟加载:对重型组件和依赖使用@defer块
- 图片优化:图片懒加载、使用现代格式(WebP、AVIF)
- 包体积分析:定期通过webpack分析包体积,定位冗余代码
- 策略性预加载:预加载关键路由,其余路由懒加载
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
- 严格模式:启用并维护TypeScript严格模式
- 严格空检查:明确处理undefined/null,绝不允许隐式传递
- API文档:公共方法和接口添加清晰的JSDoc注释
- 配置集中化:特性开关、环境配置统一管理
- 自动化 lint:使用Angular特定规则的ESLint并启用自动修复
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
- 单元测试:使用Jest或Vitest测试组件与服务
- 集成测试:使用Cypress或Playwright测试关键用户流程
- 测试覆盖率:业务逻辑测试覆盖率目标80%+
- 组件测试:使用Angular Testing Library进行行为测试
- E2E冒烟测试:每次部署自动执行冒烟测试