"Instead of applying optimizations blindly, let's investigate:
Component boundaries,
State ownership,
Render frequency,
Change Detection triggers,
Reactive architecture,
Performance becomes a design discussion."
In Angular 22, this architectural statement is no longer just theoretical advice—it is how the framework natively forces you to build. In v22, many experimental features have officially graduated to a stable, signal-first ecosystem, making performance a structural design requirement from day one. [1]
Applying the core statement to the modern lens of Angular 22 looks like this:
1. Component Boundaries: The "Standalone First" Default
The Design Discussion: Angular 22 moves completely away from legacy architecture. When planning component boundaries, teams discuss isolated data scoping. [1]
The v22 Reality: Components are modular, completely self-contained units that enforce sharp structural boundaries. You intentionally separate parent containers from child views to map local change detection boundaries seamlessly. [1, 2]
2. State Ownership: Stable Signal Forms & @Service
The Design Discussion: Instead of mixing template state with enterprise data, developers precisely map state owners using localized primitives. [1]
The v22 Reality: Signal Forms are fully stable. You no longer manage form state via verbose, heavy FormGroup configurations that require massive boilerplate. State is owned directly by localized, type-safe signals connected smoothly to the view layer. Meanwhile, shared architectural state uses the cleaner @Service() decorator for tree-shakeable singletons. [1, 2, 3]
3. Render Frequency: Modern @for Loops and Debouncing
The Design Discussion: Tracking how often a template re-evaluates is critical to avoiding micro-lag.
The v22 Reality: Developers optimize render loops by ensuring templates only process clean values. Type-checking for @for blocks is caught at build-time rather than runtime. Additionally, native utility methods like debounced() allow you to rate-limit signal emissions natively, explicitly lowering the frequency at which expensive calculations or re-renders run. [1, 2]
4. Change Detection Triggers: OnPush by Default (The Shift From "Eager")
The Design Discussion: Performance becomes a design discussion because Angular 22 radically flips the default behavior of the framework. [1]
The v22 Reality: OnPush is the new default change detection strategy for all new components. The old, aggressive checking behavior is now labeled "Eager". You no longer code blindly and add OnPush at the end to save performance. Instead, you design your data flows assuming the component will not check for updates unless its @Input() reference strictly changes or a signal emits. [1, 2, 3]
5. Reactive Architecture: Production-Ready Async Signals (httpResource)
The Design Discussion: The app is built as a complete, unified reactive graph rather than a series of manual, disconnected RxJS subscriptions that cause memory leaks. [1, 2]
The v22 Reality: The Resource API (resource and httpResource) is production-stable. Instead of writing complex switchMap sequences in RxJS to fetch API data when an ID changes, data fetching is mapped as an asynchronous, reactive derivation. The framework automatically links the UI signal, handles network race conditions, and surgically patches the DOM. [1, 2, 3]
The Four Main Layers
Backend/API: This is the external source where your data lives, typically accessed through services using HttpClient or the signal-based httpResource.
Global Signal Store: Created using the signalStore function, this is a "singleton" (a single shared instance) provided at the root level of your app. It serves as the central hub for your application's state and logic.
Smart Components: These are feature-level components that "inject" the store to coordinate data. They don't handle UI details themselves; instead, they express the user's "intentions" by calling store methods.
Dummy Components: Also called "presentational components," these focus only on how things look. They are decoupled from the store and communicate only with their parent Smart Components through Inputs and Outputs.
How the Store Works Internally
The Store uses three main features to manage data:
withState: This initializes the raw data (the "source of truth") as a set of signals.
withMethods: This defines the public API for the store. It handles logic and side effects, such as making asynchronous calls to the Backend.
withComputed: This creates "view models" or derived signals. Think of these as formulas that automatically recalculate whenever the underlying state changes.
patchState: This is the specific mechanism used to update the store's state immutably, ensuring that changes are predictable and easy to track.
The Cyclic Data Flow
The data flow follows a predictable loop:
User Action: A user interacts with a Dummy Component (like clicking a button).
Output to Smart: The Dummy Component emits an event (Output) to the Smart Component.
Method Call: The Smart Component calls a method on the Global Signal Store to express what the user wants to do.
Backend Fetch: The Store performs an asynchronous task, such as fetching fresh data from the Backend.
State Update: The Store uses patchState to update the data signals.
Signal Flow Down: The new data flows automatically "downward" through Computed Signals to the Smart Component.
Input to Dummy: Finally, the data is passed into the Dummy Component via an Input signal to refresh what the user sees on the screen