Rajnish Noonia

Tag: RxJS

  • Building a TypeScript-First Frontend SDK: An Architectural Journey

    Over the past few years I have spent much of my time on one recurring problem: how do you let many teams build many data-intensive web applications without each of them reinventing the same foundations? This post walks through the architectural journey of designing a TypeScript-first frontend SDK to answer that question. It focuses on the patterns and trade-offs rather than any particular implementation, because the ideas travel well beyond the project that prompted them.

    Application layer Individual applications built on the platform Data grid apps Dashboards Workflow tools Admin tools Others… Framework layer Native React and Angular — thin bindings over the core Angular bindings Components · Directives · DI bridge React bindings Components · Hooks · DI bridge Core Framework-agnostic TypeScript — no UI framework dependency MVVMViewModel · State IoC / DIContainer · Discovery Data gridConfig · Streaming FormsRules · Validation ShellConfig-driven LayoutPanels · Persist ThemingTokens · Dark mode MessagingEvents · Notifications VisualisationCharts · Maps StreamingObservables ObservabilityLogging · Tracing · APM SecurityAuth · Permissions AI-ready contextContext files · Agent rules Scaffolding CLI + delivery pipeline CI/CD · Containers · AWS · dev / test / prod environments
    A layered SDK: framework-agnostic core, thin React/Angular bindings, and applications on top

    The Starting Point: Why a Component Library Is Not Enough

    The usual answer to “we need consistency across teams” is a component library: styled buttons, inputs and grids published as a package. In my experience that solves perhaps a fifth of the real problem. It says nothing about where state lives, how view logic is tested without rendering, how the same feature behaves identically in two UI frameworks, how dependent form fields validate each other, or how any of it stays coherent across years of changing teams.

    So the goal shifted from “shared components” to “shared architecture”: a TypeScript-first, framework-agnostic SDK in which React and Angular are equal, native rendering targets. The choice of view framework becomes a team preference, not an architectural fork.

    Principle 1: MVVM Keeps the View Thin

    The most important decision was to adopt Model–View–ViewModel as the backbone. The view is a template that binds to properties and commands. The ViewModel is a plain TypeScript class that owns state and behaviour. The model is data and services. Nothing else is allowed to blur those lines.

    Three benefits follow. Testability: a ViewModel has no JSX and no decorators, so business logic is tested as ordinary unit tests instead of by mounting components and poking the DOM. Reuse across frameworks: the same ViewModel drives a React view and an Angular view, so behaviour is written once. Clarity: engineers always know where logic belongs, which removes a whole category of code-review debate.

    Principle 2: Dependency Injection Owned by the Core

    MVVM only works well when ViewModels can ask for what they need without knowing where it comes from. Neither framework’s native mechanism fits: Angular’s injector only exists in Angular, and React context is not really DI. The answer was an IoC container that lives in the core, independent of any UI framework, with thin bridges into each one – a hook-based resolver for React and an injector bridge for Angular.

    Services are registered against interfaces, and can be discovered by capability rather than concrete type. This is where SOLID stops being a slide and becomes the path of least resistance: new behaviour arrives as a new registration, not an edit to existing classes, and every dependency can be swapped for a test double.

    Principle 3: Layers With Enforced Boundaries

    The SDK is organised as a monorepo with three layers:

    • Core: pure TypeScript – MVVM primitives, the container, state, messaging, streaming, configuration, theming tokens and platform services. No UI framework dependency.
    • Framework bindings: thin React and Angular packages that render the core’s concepts natively and bridge the container into each framework.
    • Applications: products built on a shared, configuration-driven shell, deployable to the browser or to desktop containers.

    Build-time boundary rules stop applications from reaching into the core directly and reject circular dependencies. Architecture that is only documented erodes; architecture that fails the build stays intact.

    Principle 4: Solve Cross-Cutting Concerns Once

    Most of the value of a platform is in the unglamorous capabilities that every application needs and no team wants to build. Rather than leave them to each project, the SDK provides them as shared services:

    • Configuration-driven shell and layout: navigation, panels and workspaces declared in configuration, with dockable panels whose arrangement is persisted per user.
    • State persistence: any screen can opt in to having column widths, filters, sort order and layout restored on reload.
    • Data grid abstraction: grids configured declaratively, with streaming updates delivered through observables, so teams write configuration and a ViewModel rather than grid plumbing.
    • Forms with a rule engine: field dependencies, visibility and validation expressed as rules; client and server validation surface through one pipeline.
    • Design tokens: semantic colours, spacing and typography exposed as typed constants and CSS variables, with dark mode as a first-class theme.
    • Observability and security: structured logging, tracing and application performance monitoring wired in by default, alongside authentication and permission handling.

    Principle 5: Start Every Project From Working, Not Blank

    A scaffolding CLI turns the SDK into a starting point rather than a set of instructions. A few commands produce an application that already has the shell, theming, authentication, observability, CI/CD pipelines, container configuration and cloud deployment templates for AWS across dev, test and production environments. The decisions that usually consume a project’s first sprint are already made, and every project starts in the same shape – which makes moving between them much easier for engineers.

    Principle 6: Design for AI-Assisted Development

    A newer lesson: an SDK now has two audiences, human developers and AI coding agents. Each package carries a concise context file describing its patterns, naming conventions and rules, and a routing structure points an agent at only the context relevant to its task. Generated projects inherit these files automatically.

    Treating that context as a maintained deliverable – updated alongside code and checked in CI – keeps agents inside the architecture instead of inventing their own. The result is AI assistance that accelerates teams without quietly eroding standards.

    Principle 7: Governance That Scales

    • Enforced module boundaries at build time
    • Semantic versioning with deprecation windows, so nothing disappears without warning
    • Living documentation: component catalogues and visual regression tests in the pipeline
    • Context files as a contract for both humans and agents

    What I Took Away

    The measurable outcome was that applications which once took months could be delivered in weeks, with a consistent look, feel and behaviour. But the more lasting lessons were architectural:

    • Separate behaviour from rendering early; it is very hard to retrofit.
    • Own your dependency injection in the core if you want framework independence.
    • Encode rules in the build, not in wiki pages.
    • Invest in the boring cross-cutting services – that is where teams lose the most time.
    • Treat AI context as part of the product, not an afterthought.

    A good platform is measured by how rarely the teams building on it need to think about it.

  • Reactive Extensions (Rx) Data Streaming

    You have probably heard about Reactive Extensions, a library from Microsoft that greatly simplifies working with asynchronous data streams and allows to query them with LINQ operators.In my previous post I briefly discussed about loading data via entity framework asynchronously however it lacks getting chunks of data. This post demonstrates how to use Reactive Extensions for loading data from database asynchronously in chunks covering brief around Reactive extension library.

    Reactive extensions (Rx)
    The Reactive Extensions (Rx) is a library for composing asynchronous and event-based programs using observable sequences and LINQ-style query operators. Using Rx, developers represent asynchronous data streams with Observables, query asynchronous data streams using LINQ operators, and parameterize the concurrency in the asynchronous data streams using Schedulers. Simply put, Rx = Observables + LINQ + Schedulers.Data sequences can take many forms, such as a stream of data from a file or web service, web services requests, system notifications, or a series of events such as user input. Reactive Extensions represents all these data sequences as observable sequences. An application can subscribe to these observable sequences to receive asynchronous notifications as new data arrive. The Rx library is available for desktop application development in .NET. It is also released for Silverlight, Windows Phone 7 and JavaScript.

    Reactive programming allows you to turn those aspects of your code that are currently imperative into something much more event-driven and flexible.Reactive programming can be applied to a range of situations—from WPF applications to Windows Phone apps—to improve coding efficiency and boost performance.

    Code snippets to asynchronous load data via entity framework in batches of 200 records.

    public void LoadPostCodes()
    {
     btnStatus.Content = "Started";
     listBox1.Items.Clear();
     (from p in cx.MAS_PostCode select p)
     .ToObservable(Scheduler.NewThread)
     .Buffer(200)
     .ObserveOn(SynchronizationContext.Current)
     .Subscribe(ld =>
     {
     foreach (var item in ld)
     {
     ListBoxItem litem = new ListBoxItem();
     litem.Content = string.Format("{0} {1}", item.PC_PostCode, item.PC_Address1);
     listBox1.Items.Add(litem);
     listBox1.ScrollIntoView(litem);
     }
     button1.Content = listBox1.Items.Count.ToString();
     },
     () => { btnStatus.Content = "Finished"; }
     );
    }

    Have finished writing MVVM(Nano View model) based scenario & Rx implementations …

    Will write soon and publish code..

    Regards
    Rajnish