Scalable JavaScript Single-Page Application Architecture Using Angular2, RxJS, and NgRx
This article describes a scalable architecture for large Angular2 single‑page applications, covering communication layers, predictable state management with Redux/NgRx, modular design, lazy loading, dependency injection, and provides code examples illustrating models, components, and services for both single‑player and multiplayer scenarios.
The article outlines a scalable architecture for a dynamic single‑page application (SPA) built with Angular2, targeting a fast‑growing startup product that experiences frequent business changes.
Requirements include a stable core domain with communication needs to users and RESTful APIs, optional P2P links, real‑time server communication, and support for multiple protocols (HTTP, WebSocket, UDP/WebRTC) using JSON, JSON‑RPC, and BERT formats.
To handle asynchronous events across services, RxJS is recommended for multiplexing data streams, hot‑observers, and declarative transformations.
Predictable State Management is achieved with Redux (ngrx) and TypeScript for type safety, enabling versioned stores and robust handling of side effects.
Module Design abstracts functionality into a facade (model) layer that mediates between view components and services, allowing developers of varying experience to work on isolated modules. The architecture diagram shows a view layer, facade (model) layer, reducers, and state.
Context‑dependent implementations use dependency injection so that components like SinglePlayerComponent and MultiPlayerComponent can interact with appropriate services (GameServer, GameP2PService) via shared models.
Lazy Loading is essential as the codebase can exceed 50,000 lines. The proposed directory structure separates multi‑player, single‑player, home, and shared code, loading modules on demand.
Additional architectural requirements include testability, maintainability, and adherence to the open/closed principle.
Technology Stack chosen: Angular 2, RxJS, and NgRx. The solution is framework‑agnostic and could be implemented with React as well.
A demo repository (https://github.com/mgechev/scalable-architecture-demo) provides an Angular2/RxJS implementation of a typing‑speed game with single‑player and multiplayer modes, illustrating the architecture in practice.
View Component Example shows an Angular component with inputs, outputs, and interaction with a GameModel facade:
@Component({
// Some component-specific declarations
providers: [GameModel]
})
export class GameComponent implements AfterViewInit {
@Input() text: string;
@Output() end: EventEmitter
= new EventEmitter
();
@Output() change: EventEmitter
= new EventEmitter
();
constructor(private _model: GameModel, private _renderer: Renderer) {}
ngAfterViewInit() { this._model.startGame(); }
changeHandler(data: string) {
if (this.text === data) {
this.end.emit(this.timer.time);
this._model.completeGame(this.timer.time, this.text);
this.timer.reset();
} else {
this._model.onProgress(data);
}
}
// ...other methods
}The component highlights input/output APIs, internal state handling, and use of GameModel as a facade.
Model Definition demonstrates an injectable GameModel that selects state slices from the NgRx store, dispatches actions, and performs async operations:
@Injectable()
export class GameModel extends Model {
games$: Observable
;
game$: Observable
;
constructor(protected _store: Store
, @Optional() @Inject(AsyncService) _services: AsyncService[]) {
super(_services || []);
this.games$ = this._store.select('games');
this.game$ = this._store.select('game');
}
startGame() { this._store.dispatch(GameActions.startGame()); }
onProgress(text: string) {
this.performAsyncAction(GameActions.gameProgress(text, new Date()))
.subscribe(() => {}, data => { if (data.invalidGame) this._store.dispatch(GameActions.invalidateGame()); });
}
completeGame(time: number, text: string) {
const action = GameActions.completeGame(time, text);
this._store.dispatch(action);
this.performAsyncAction(action).subscribe(() => console.log('Done!'));
}
}This model abstracts store interactions, allowing UI developers to focus on components without worrying about communication protocols or Redux internals.
Architecture Digest
Focusing on Java backend development, covering application architecture from top-tier internet companies (high availability, high performance, high stability), big data, machine learning, Java architecture, and other popular fields.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.