NgRx Signal Store vs Klasik NgRx 2026: Hangisini Secmelisiniz?

Angular durum yonetimi icin NgRx Signal Store ve Klasik NgRx kapsamli karsilastirmasi. Pratik kod ornekleri ve 2026 tavsiyeleri.

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. Bu karsilastirma, Angular gelistiricilerinin 2026 yilinda bilinçli mimari kararlar vermelerine yardimci olmak için her iki çözümü incelemektedir.

Hizli Karar Rehberi

Boilerplate azaltmanin önemli oldugu özellik düzeyinde durum ve orta karmasiklikta uygulamalar 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ön | Klasik NgRx | Signal Store | |-----|-------------|---------------| | Zihinsel Model | Redux/Flux | Reaktif Servisler | | Boilerplate | Yüksek (aksiyonlar, reducer'lar, selectorlar, efektler) | Düsük (durum, metodlar, computed) | | Reaktivite | RxJS Observable'lar | Angular Signal'lar | | DevTools | Tam Redux DevTools | Signal Store DevTools (eklenti ile) | | Ögrenme Egrisi | Dik | Orta | | Bundle Boyutu | Daha Büyük | Daha Küçük |

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 pahesina ö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 artir 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.

withEntities ile Varlık 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 varlık 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.

with-request-status.tstypescript
import { computed } from '@angular/core';
import { signalStoreFeature, withComputed, withState } 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'),
    }))
  );
}

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.

Bilesken 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 algilimasiyla bütünlesir.

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.

Klasikten Signal Store'a Migrasyon Stratejisi

Mevcut bir klasik NgRx uygulamasini tasinak büyük bir yeniden yazip 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 tasinak
  • 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
Birlikte Varlik Çalisiyor

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 20+ uygulamalari olusturma
  • Özellik düzeyinde veya bilesen düzeyinde durum yönetimi
  • Gelistirici deneyimi ve azaltilmis boilerplate önceliklendirme
  • Ekibin sinirli NgRx deneyimi var
  • Bundle boyutu önemli

Klasik NgRx'i su durumlarda seçin:

  • Mevcut NgRx uygulamalarinin bakami
  • 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

Her iki yaklasimda da durum yönetimi temellerini anlamak, gelistiricileri çesitli Angular kod tabanlarina hazirlar.

Pratik yapmaya başla!

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

Sonuç

  • Signal Store, klasik NgRx'e kiyasla durum yönetimi boilerplate'ini %60-70 azaltir
  • 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
  • Signal Store'un computed signal'lari açik selector tanimlari olmadan otomatik memoizasyon saglar
  • Özel store özellikleri (signalStoreFeature) yeniden kullanilabilir kesisen ilgi alanlari saglar
  • 2026'da yeni Angular 20+ projeleri için, belirli gereksinimler klasik NgRx'i gerektirmedikçe Signal Store varsayilan seçim olmalidir

Etiketler

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

Paylaş

İlgili makaleler