platform-main — composition-over-inheritance redesign

Six classes stopped extending RxJS types. Here's exactly what each one gained and lost.

The real @ngrx/store/@ngrx/effects extend RxJS's Observable/ Subject/BehaviorSubject directly on six classes across two modules — a genuine Interface Segregation violation, not a style nitpick. Every consumer inherits the entire RxJS operator surface (pipe, lift, toPromise, forEach…) when each class's actual contract is far narrower. Fixed one class at a time, in dependency order, fully verified before moving to the next.

Before → After, All Six Classes

Class
Before
After
ActionsSubjectstore
extends BehaviorSubject<Action>
next/subscribe/error/complete/asObservable only — no pipe, not instanceof Observable
ReducerManagerstore
extends BehaviorSubject<ActionReducer>
Composes it privately; ReducerObservable DI token still exposes the Observable view for consumers that need one
Storestore
extends Observable<T> implements Observer<Action>, overrides lift() to stop RxJS silently downgrading it
select() keeps its full overload set, returns a plain Observable off a new state$ field — next/error/complete/subscribe/pipe all removed, a real capability cut
Statestore
extends BehaviorSubject<any>
Composes a private BehaviorSubject, exposes state$/state/a value getter
ScannedActionsSubjectstore
extends Subject<Action> implements OnDestroy
Composes a private Subject; found in round 3 of the audit — a multi-line class declaration the first two grep passes missed
EffectSourceseffects
extends Subject<any>
Composes a private Subject; contract narrowed to addEffects() in, toActions() out

Why composition, not a workaround

Each class now holds its RxJS subject privately instead of extending it, exposing only the surface its actual contract needs. No override-the-base-class special case left behind — Store's old lift() override, the symptom that flagged the problem in the first place, is gone because the cause is gone, not patched around.

What it actually cost

Store losing the Observer interface is a real behavior change: ~40 call sites across 6 spec files rewritten, 2 tests deleted outright because the capability they tested no longer exists to test. Every module added afterward that depends on storeentity, effects, router-store, store-devtools, data — hit the same ripple at least once, caught by the build, not by inspection.

Deliberately left alone: ReducerObservable and Actions (effects) both still extend Observable — their entire contract is being pipeable, with no unrelated method bolted on, so there's no interface to segregate. Composition was applied to six classes because six classes had the actual violation, not as a blanket "never extend RxJS" rule — the two exceptions are the evidence the decision was reasoned, not reflexive. See docs/architecture.md for the full ADR write-up.