NgRx Signal Store vs Klasik NgRx 2026: Hangisini Seçmelisiniz?

Angular durum yönetimi için NgRx Signal Store ve Klasik NgRx kapsamli karsilastirmasi. NgRx 22 özellikleri ile pratik kod örnekleri ve tavsiyeler.

NgRx Signal Store vs Classic NgRx karsilastirma

NgRx Signal Store, geleneksel Redux tabanli aksiyonlar-reducer'lar-selectorlar kalibini sinyal tabanli reaktif bir yaklasimla degistirerek Angular durum yonetiminde temel bir degisimi temsil etmektedir. NgRx 22 (Agustos 2026'da yayinlandi) ile Signal Store, kaynak uzantilari, dinamik deep signal'lar ve iyilestirilmis birlestirilebilirlik kazanmaktadir. Bu karsilastirma, Angular gelistiricilerinin bilinçli mimari kararlar vermelerine yardimci olmak için her iki çözümü incelemektedir.

Hizli Karar Rehberi

Özellik düzeyinde durum, orta karmasiklikta uygulamalar ve Angular'in yerel Resource API entegrasyonu için Signal Store kullanilmalidir. Siki denetim izleri, karmasik yan etkiler ve kapsamli DevTools hata ayiklamasi gerektiren kurumsal uygulamalar için Klasik NgRx kullanilmalidir.

Mimari Farkliliklari Anlama

Klasik NgRx, Redux kalibi üzerine kuruludur: aksiyonlar olaylari tanimlar, reducer'lar yeni degismez durum üretir ve selectorlar verileri çikarir. Her durum degisikligi öngörülebilir, izlenebilir bir boru hattindan geçer.

Signal Store farkli bir yaklasim benimser. Aksiyonlari reducer'lar araciligiyla göndermek yerine, durum reaktif sinyallerde yasir. Güncellemeler patchState() çagiran metodlar araciligiyla gerçeklesir ve türetilmis degerler memoize edilmis selectorlar yerine computed signal'lardan gelir.

YönKlasik NgRxSignal Store
Zihinsel ModelRedux/FluxReaktif Servisler
BoilerplateYüksek (aksiyonlar, reducer'lar, selectorlar, efektler)Düsük (durum, metodlar, computed)
ReaktiviteRxJS Observable'larAngular Signal'lar
Resource APIManuel entegrasyon@ngrx/signals/resource ile yerel
DevToolsTam Redux DevToolsSignal Store DevTools (eklenti ile)
Ögrenme EgrisiDikOrta
Bundle BoyutuDaha Büyük (~15-20KB gzip)Daha Küçük (~4KB gzip)

Angular Signals'a zaten asina olan gelistiriciler için Signal Store, framework'ün reaktif ilkellerinin dogal bir uzantisi gibi hissedilir.

Klasik NgRx: Angular'da Redux Kalibi

Klasik NgRx, durum yönetimini aksiyonlar, reducer'lar, selectorlar ve efektler etrafinda organize eder. Bu ayrim, uzunluk pahasina öngörülebilirligi zorlar.

books.actions.tstypescript
import { createActionGroup, emptyProps, props } from '@ngrx/store';
import { Book } from './book.model';

export const BooksActions = createActionGroup({
  source: 'Books',
  events: {
    'Load Books': emptyProps(),
    'Load Books Success': props<{ books: Book[] }>(),
    'Load Books Failure': props<{ error: string }>(),
    'Add Book': props<{ book: Book }>(),
    'Remove Book': props<{ id: string }>(),
  },
});

createActionGroup API'si ilgili aksiyonlari tek bir kaynak altinda gruplar, DevTools okunabilirligini artirir ve ayri createAction çagrilarina kiyasla boilerplate'i azaltir.

books.reducer.tstypescript
import { createReducer, on } from '@ngrx/store';
import { BooksActions } from './books.actions';
import { Book } from './book.model';

export interface BooksState {
  books: Book[];
  loading: boolean;
  error: string | null;
}

const initialState: BooksState = {
  books: [],
  loading: false,
  error: null,
};

export const booksReducer = createReducer(
  initialState,
  on(BooksActions.loadBooks, (state) => ({
    ...state,
    loading: true,
    error: null,
  })),
  on(BooksActions.loadBooksSuccess, (state, { books }) => ({
    ...state,
    books,
    loading: false,
  })),
  on(BooksActions.loadBooksFailure, (state, { error }) => ({
    ...state,
    loading: false,
    error,
  }))
);

Reducer'lar yeni durum nesneleri döndüren saf fonksiyonlar olarak kalir. on() fonksiyonu aksiyon eslestirmeyi ve durum güncellemelerini özlü bir sekilde yönetir.

books.selectors.tstypescript
import { createFeatureSelector, createSelector } from '@ngrx/store';
import { BooksState } from './books.reducer';

export const selectBooksState = createFeatureSelector<BooksState>('books');

export const selectAllBooks = createSelector(
  selectBooksState,
  (state) => state.books
);

export const selectBooksLoading = createSelector(
  selectBooksState,
  (state) => state.loading
);

export const selectPublishedBooks = createSelector(
  selectAllBooks,
  (books) => books.filter((book) => book.published)
);

Selectorlar otomatik olarak memoizasyon saglar. selectPublishedBooks selectoru sadece kitaplar dizisi degistiginde yeniden hesaplanir, bu da sik güncellenen store'larda bile okumalari verimli kilar.

NgRx Signal Store: Sinyal Öncelikli Durum Yönetimi

Signal Store, durumu, computed degerleri ve metodlari tek bir enjekte edilebilir servis içinde birlestirir. Resmi NgRx dokümantasyonu bunu "Angular Signals için yerel destekli, tam özellikli bir durum yönetimi çözümü" olarak tanimlar.

books.store.tstypescript
import { computed, inject } from '@angular/core';
import { patchState, signalStore, withComputed, withMethods, withState } from '@ngrx/signals';
import { BooksService } from './books.service';
import { Book } from './book.model';

interface BooksState {
  books: Book[];
  loading: boolean;
  error: string | null;
}

const initialState: BooksState = {
  books: [],
  loading: false,
  error: null,
};

export const BooksStore = signalStore(
  { providedIn: 'root' },
  withState(initialState),
  withComputed(({ books }) => ({
    publishedBooks: computed(() => books().filter((book) => book.published)),
    totalBooks: computed(() => books().length),
  })),
  withMethods((store, booksService = inject(BooksService)) => ({
    async loadBooks() {
      patchState(store, { loading: true, error: null });
      try {
        const books = await booksService.getAll();
        patchState(store, { books, loading: false });
      } catch (error) {
        patchState(store, { loading: false, error: 'Failed to load books' });
      }
    },
    addBook(book: Book) {
      patchState(store, ({ books }) => ({ books: [...books, book] }));
    },
    removeBook(id: string) {
      patchState(store, ({ books }) => ({
        books: books.filter((book) => book.id !== id),
      }));
    },
  }))
);

Tüm özelligin durum yönetimi tek bir dosyaya sigar. Durum özellikleri otomatik olarak sinyal olur, computed signal'lar selectorlarin yerini alir ve metodlar aksiyon gönderimlerinin yerini alir.

NgRx 22: Kaynak Uzantilari ve withProps

NgRx 22, Angular'in Resource API'si ile dogrudan entegrasyonu saglayan @ngrx/signals/resource giris noktasini tanitir. withProps özelligi servis enjeksiyonunu merkezilestirirken, kaynak uzantilari yaygin asenkron kaliplari ele alir.

books-resource.store.tstypescript
import { computed, inject } from '@angular/core';
import { patchState, signalStore, withComputed, withMethods, withProps } from '@ngrx/signals';
import { extendResource, withPreviousValueOnLoading, withValueOnError } from '@ngrx/signals/resource';
import { httpResource } from '@angular/core';
import { BooksService } from './books.service';
import { Book } from './book.model';

export const BooksResourceStore = signalStore(
  { providedIn: 'root' },
  // Servisleri withProps ile enjekte et (alt çizgi ile özel kural geregi)
  withProps(() => ({
    _booksService: inject(BooksService),
  })),
  // Uzantilarla kaynaklari yapilandir
  withProps((store) => ({
    _booksResource: extendResource(
      httpResource<Book[]>({
        url: '/api/books',
        defaultValue: [],
      }),
      withPreviousValueOnLoading(),  // Yeniden yüklerken son degeri korur
      withValueOnError([])           // Hata durumunda bos dizi döndürür
    ),
  })),
  // Tüketicilere salt okunur kaynak sun
  withProps((store) => ({
    booksResource: store._booksResource.asReadonly(),
  })),
  withComputed((store) => ({
    books: computed(() => store.booksResource.value()),
    isLoading: computed(() => store.booksResource.isLoading()),
    publishedBooks: computed(() => 
      store.booksResource.value().filter((book) => book.published)
    ),
  }))
);

Kaynak uzantilari iki yaygin sorunu çözer: yeniden yüklemelerde titreme (withPreviousValueOnLoading ile düzeltilir) ve hata durumu yönetimi (withValueOnError veya withPreviousValueOnError ile düzeltilir). Bu uzantilar birlestirilebilir ve provideResourceExtensions ile global olarak saglanabilir.

withEntities ile Varlik Yönetimi

Her iki yaklasim da varlik koleksiyonlarini destekler, ancak Signal Store'un withEntities özelligi kodu önemli ölçüde azaltir.

books-entity.store.tstypescript
import { computed, inject } from '@angular/core';
import { patchState, signalStore, withComputed, withMethods } from '@ngrx/signals';
import {
  addEntity,
  removeEntity,
  setAllEntities,
  updateEntity,
  withEntities,
} from '@ngrx/signals/entities';
import { BooksService } from './books.service';
import { Book } from './book.model';

export const BooksEntityStore = signalStore(
  { providedIn: 'root' },
  withEntities<Book>(),
  withComputed(({ entities }) => ({
    publishedBooks: computed(() => entities().filter((book) => book.published)),
  })),
  withMethods((store, booksService = inject(BooksService)) => ({
    async loadBooks() {
      const books = await booksService.getAll();
      patchState(store, setAllEntities(books));
    },
    addBook(book: Book) {
      patchState(store, addEntity(book));
    },
    updateBook(id: string, changes: Partial<Book>) {
      patchState(store, updateEntity({ id, changes }));
    },
    removeBook(id: string) {
      patchState(store, removeEntity(id));
    },
  }))
);

withEntities özelligi otomatik olarak ids, entityMap ve entities sinyallerini saglar. addEntity, removeEntity ve updateEntity gibi varlik güncelleyicileri koleksiyon mutasyonlarini yönetir.

Angular mülakatlarında başarılı olmaya hazır mısın?

İnteraktif simülatörler, flashcards ve teknik testlerle pratik yap.

Yeniden Kullanilabilirlik için Özel Store Özellikleri

Signal Store'un signalStoreFeature fonksiyonu, kesisen ilgi alanlarinin çikartilmasini ve farkli store'larda yeniden kullanilmasini saglar. NgRx 22, tip çikartmayi basitlestirmek için SignalStoreFeatureType ekler.

with-request-status.tstypescript
import { computed } from '@angular/core';
import { signalStoreFeature, withComputed, withState, SignalStoreFeatureType } from '@ngrx/signals';

type RequestStatus = 'idle' | 'pending' | 'fulfilled' | 'error';

export function withRequestStatus() {
  return signalStoreFeature(
    withState<{ requestStatus: RequestStatus }>({ requestStatus: 'idle' }),
    withComputed(({ requestStatus }) => ({
      isPending: computed(() => requestStatus() === 'pending'),
      isFulfilled: computed(() => requestStatus() === 'fulfilled'),
      isError: computed(() => requestStatus() === 'error'),
    }))
  );
}

// NgRx 22: manuel yeniden bildirim olmadan tip çikartma
export type RequestStatusFeature = SignalStoreFeatureType<typeof withRequestStatus>;

export function setPending(): { requestStatus: RequestStatus } {
  return { requestStatus: 'pending' };
}

export function setFulfilled(): { requestStatus: RequestStatus } {
  return { requestStatus: 'fulfilled' };
}

export function setError(): { requestStatus: RequestStatus } {
  return { requestStatus: 'error' };
}

Bu özel özellik artik yükleme durumu izlemesi gerektiren herhangi bir store'a eklenebilir.

books-with-status.store.tstypescript
import { inject } from '@angular/core';
import { patchState, signalStore, withMethods } from '@ngrx/signals';
import { setAllEntities, withEntities } from '@ngrx/signals/entities';
import { withRequestStatus, setFulfilled, setPending, setError } from './with-request-status';
import { BooksService } from './books.service';
import { Book } from './book.model';

export const BooksStore = signalStore(
  { providedIn: 'root' },
  withEntities<Book>(),
  withRequestStatus(),
  withMethods((store, booksService = inject(BooksService)) => ({
    async loadBooks() {
      patchState(store, setPending());
      try {
        const books = await booksService.getAll();
        patchState(store, setAllEntities(books), setFulfilled());
      } catch {
        patchState(store, setError());
      }
    },
  }))
);

Birlesmis store artik mantigi çogaltmadan isPending(), isFulfilled() ve isError() computed signal'larini açiga çikarir.

watchState ile Senkron Durum Izleme

NgRx 22, geri alma/yineleme islevi veya durum geçmisi için yararli olan senkron durum degisikligi izleme için watchState fonksiyonunu ekler.

state-history.tstypescript
import { inject, DestroyRef } from '@angular/core';
import { watchState } from '@ngrx/signals';
import { BooksStore } from './books.store';

export function setupStateHistory() {
  const store = inject(BooksStore);
  const destroyRef = inject(DestroyRef);
  const history: unknown[] = [];

  // Senkron izleme: her durum degisikligini yakalar, birlestirme yok
  watchState(store, (state) => {
    history.push(structuredClone(state));
  });

  return { history };
}

Güncellemeleri birlestiren efekt tabanli izlemenin aksine, watchState her patchState çagrisinda senkron olarak tetiklenir ve tam durum geçmisini yakalar.

Bilesen Entegrasyon Kaliplari

Signal Store, sinyal okumalari araciligiyla Angular'in sablon sözdizimi ile dogal olarak bütünlesir.

books-list.component.tstypescript
import { Component, inject } from '@angular/core';
import { BooksStore } from './books.store';

@Component({
  selector: 'app-books-list',
  standalone: true,
  template: `
    @if (store.isPending()) {
      <div class="loading-spinner">Kitaplar yükleniyor...</div>
    }

    @if (store.isError()) {
      <div class="error-message">Kitaplar yüklenemedi</div>
    }

    @for (book of store.entities(); track book.id) {
      <div class="book-card">
        <h3>{{ book.title }}</h3>
        <p>{{ book.author }}</p>
        <button (click)="store.removeBook(book.id)">Kaldir</button>
      </div>
    }

    <p>Toplam: {{ store.totalBooks() }} kitap</p>
  `,
})
export class BooksListComponent {
  readonly store = inject(BooksStore);

  constructor() {
    this.store.loadBooks();
  }
}

Abonelik yönetimi yok. Async pipe'lar yok. Sinyaller, optimum performans için Angular'in zoneless degisiklik algilamasi ile bütünlesir. Angular 22'nin varsayilan OnPush degisiklik algilama stratejisi ile Signal Store bilesenleri hassas, granüler güncellemelerden yararlanir.

Performans ve Bundle Boyutu Degerlendirmeleri

Signal Store'un daha hafif mimarisi daha küçük bundle'lara dönüsür. Çekirdek @ngrx/signals paketi yaklasik 4KB gzip eklerken, tam klasik NgRx yigini (@ngrx/store, @ngrx/effects, @ngrx/entity) 15-20KB ekler.

Signal Store ayrica Angular'in sinyal tabanli degisiklik algilamasindan da yararlanir. Computed signal'lar yalnizca bagimliliklar degistiginde yeniden hesaplanir ve bilesenler yalnizca tüketilen sinyaller güncellendiginde yeniden render edilir. Bu granüler reaktivite, Observable tabanli kaliplarda yaygin olan gereksiz degisiklik algilama döngülerini ortadan kaldirir.

Halihazirda yogun RxJS kullanan uygulamalar için, klasik NgRx'in Observable tabanli selectorlari mevcut boru hatlariyla sorunsuzca bütünlesir.

NgRx 22 Gereksinimleri

NgRx 22, Angular 22.x, TypeScript 6.0.x ve RxJS 6.5.x veya 7.5.x gerektirir. Signal Store projeleri için ng update @ngrx/signals@22 ile güncelleme yapin.

Klasikten Signal Store'a Migrasyon Stratejisi

Mevcut bir klasik NgRx uygulamasini tasimak büyük bir yeniden yazim gerektirmez. Her iki çözüm bir arada var olabilir ve kademeli benimsemeyi mümkün kilar.

  • Signal Store kullanarak yeni özelliklerle baslamak
  • Izole özellik modüllerini birer birer tasimak
  • Migrasyon maliyeti faydasini asarsa, karmasik, efekt agirlikli özellikleri klasik NgRx'te tutmak
  • Observable tabanli ve sinyal tabanli durum arasinda köprü kurmak için NgRx Signals interop yardimci programlarini kullanmak

Klasik NgRx Store ve Signal Store ayni uygulamada yan yana çalisabilir. NgRx ekibi her iki yaklasimi da resmi olarak destekler ve aralarinda köprü kurmak için interop yardimci programlari saglar.

Her Yaklasimi Ne Zaman Seçmeli

Signal Store'u su durumlarda seçin:

  • Yeni Angular 22+ uygulamalari olusturma
  • Özellik düzeyinde veya bilesen düzeyinde durum yönetimi
  • Angular'in Resource API ve Signal Forms ile entegrasyon
  • Gelistirici deneyimi ve azaltilmis boilerplate önceliklendirme
  • Ekibin sinirli NgRx deneyimi var
  • Bundle boyutu önemli

Klasik NgRx'i su durumlarda seçin:

  • Mevcut NgRx uygulamalarinin bakimi
  • Siki aksiyon günlükleme ve denetim izleri gereksinimi
  • Kapsamli yan etkilerle karmasik is akislari olusturma
  • Tam Redux DevTools zaman yolculugu hata ayiklamasi gereksinimi
  • Ekibin derin NgRx uzmanligi var

Durum yönetimi temelleri her iki yaklasimda da gelistiricileri çesitli Angular kod tabanlarina hazirlar.

Kaynaklar

Pratik yapmaya başla!

Mülakat simülatörleri ve teknik testlerle bilgini test et.

Signal Store vs Klasik NgRx: Temel Çikarimlar

  • Signal Store, klasik NgRx'e kiyasla durum yönetimi boilerplate'ini %60-70 azaltir
  • NgRx 22, sorunsuz asenkron durum yönetimi için kaynak uzantilari (withPreviousValueOnLoading, withValueOnError) ekler
  • withProps özelligi, özellikler arasinda tekrarlanan parametre listelerini ortadan kaldirarak servis enjeksiyonunu merkezilestir
  • watchState, geri alma/yineleme ve durum geçmisi için senkron durum izlemeyi saglar
  • Klasik NgRx, siki denetim izleri ve karmasik yan etkiler gerektiren kurumsal uygulamalar için daha iyi seçim olmaya devam eder
  • Her iki yaklasim ayni uygulamada bir arada var olabilir ve kademeli migrasyonu mümkün kilar
  • Yeni Angular 22+ projeleri için, belirli gereksinimler klasik NgRx'i gerektirmedikçe Signal Store varsayilan seçim olmalidir
Günün meydan okuması

Angular kodundaki hatayı bulabilir misin?

Gerçek bir kod parçası, gizli bir hata, günde bir deneme. Denemek için hesap gerekmez.

Anthony Fillion-Maillet

Yazan:

Anthony Fillion-Maillet

SharpSkill kurucusu

10 yılı aşkın süredir fullstack geliştirici. SharpSkill’i yönetiyor ve burada yayımlanan her şeyden sorumlu.

9 Ekim 2026 tarihinde güncellendi

Etiketler

#angular
#ngrx
#state-management
#signals
#typescript

Paylaş

İlgili makaleler