Building a real-time web app with Angular/Ngrx and Vert.x

Nowa­days, there are mul­ti­ple tech stacks to build a real-​time web app. What are the best choices to build real-​time An­gu­lar client apps, con­nected to a JVM-​based back­end? This ar­ti­cle de­scribes an An­gu­lar+Vertx real-​time ar­chi­tec­ture with a Proof of Con­cept demo app.

This is a re-​publication of the fol­low­ing Medium post

Intro

Wel­come to the real-​time web! It’s time to move on from tra­di­tional syn­chro­nous HTTP re­quest/re­sponse ar­chi­tec­tures to re­ac­tive apps with con­nected clients (ouch… that’s a lot of buzz­words in just one sen­tence)!

Image source: https://www.voxxed.com

To build this kind of app, Me­te­orJS is the new cool kid on the block (v1.0 re­leased in oc­to­ber 2014): a full stack Javascript plat­form to build connected-​client re­ac­tive ap­pli­ca­tions. It al­lows JS de­vel­op­ers to build and de­ploy amaz­ing mod­ern web and mo­bile apps (iOS/An­droid) in no time, using a uni­fied back­end+fron­tend code within a sin­gle app repo. That’s a pretty am­bi­tious ap­proach but it re­quires a very opin­ion­ated and highly cou­pled JS tech stack and it’s still a pretty niche frame­work.

More­over, we are a Java shop on the back­end. At Ago­ra­Pulse, we rely heav­ily on :

  • An­gu­lar and Ionic for the JS fron­tend (with a shared busi­ness/data ar­chi­tec­ture based on Ngrx),
  • Groovy and Grails ecosys­tem for the JVM back­end.

So my ques­tion is:

What are the best choices to build real-​time An­gu­lar client apps, con­nected to a JVM-​based back­end these days?

Our re­quire­ments are pretty basic. We don’t need full Me­teor’s end-​to-end ap­pli­ca­tion model. We just want to be able to :

  1. build a re­ac­tive app with an event bus on the JVM, and
  2. ex­tend the event bus down to the browser to be able to pub­lish/sub­scribe to real-​time events from an An­gu­lar app.

Server side (JVM)

Re­ac­tive apps is a hot topic nowa­days and there are many great libs/plat­forms to build this type of event-​driven ar­chi­tec­ture on the JVM:

Client side

Re­ac­tJS and An­gu­lar are the two most pop­u­lar frame­work right now to build mod­ern JS apps. Most plat­forms use SockJS to han­dle real-​time con­nec­tions:

  • Vertx-​web pro­vides a SockJS server im­ple­men­ta­tion with an event bus bridge and a vertx-​evenbus.js client li­brary (very easy to use),
  • Spring pro­vides web­socket SockJS sup­port though Spring Mes­sag­ing and Web­socket libs (see an ex­am­ple here)

Final choice: Vert.x + Angular

In the end, I’ve cho­sen to ex­per­i­ment with Vert.x for its ex­cel­lent Groovy sup­port, dis­trib­uted event bus, scal­a­bil­ity and ease of use.

I en­joyed it very much. Let me show you the re­sult of my ex­per­i­men­ta­tion which is the root of our real-​time fea­tures com­ing very soon in Ago­ra­Pulse v6.0!

Why Vert.x?

Like other re­ac­tive plat­form, Vert.x is event dri­ven and non block­ing. It scales very well (even more that Node.js).

Un­like other re­ac­tive plat­forms, Vert.x is poly­glot: you can use Vert.x with mul­ti­ple lan­guages in­clud­ing Java, JavaScript, Groovy, Ruby, Cey­lon, Scala and Kotlin.

Un­like Node.js, Vert.x is a gen­eral pur­pose tool-​kit and un­opin­ion­ated. It’s a ver­sa­tile plat­form suit­able for many things: from sim­ple net­work util­i­ties, so­phis­ti­cated mod­ern web ap­pli­ca­tions, HTTP/REST mi­croser­vices or a full blown back-​end message-​bus ap­pli­ca­tion.

Like other re­ac­tive plat­forms, it looks scary in the be­gin­ing when you read the doc­u­men­ta­tion… ;) But once you start play­ing with it, it re­mains fun and sim­ple to use, es­pe­cially with Groovy! Vert.x re­ally al­lows you to build sub­stan­tial sys­tems with­out get­ting tan­gled in com­plex­ity.

In my case, I was mainly in­ter­ested by the dis­trib­uted event-​bus pro­vided (a core fea­ture of Vert.x).

To val­i­date our ap­proach, we built pro­to­types with the fol­low­ing goals:

  • share and syn­chro­nize a com­mon (Ngrx-​based) state be­tween mul­ti­ple con­nected clients, and
  • dis­trib­ute real-​time (Ngrx-​based) ac­tions across mul­ti­ple con­nected clients, which im­pact local states/re­duc­ers.

@ngrx/store is a RxJS pow­ered state man­age­ment in­spired by Redux for An­gu­lar apps. It’s cur­rently the most pop­u­lar way to struc­ture com­plex busi­ness logic in An­gu­lar apps.

Redux

Source: https://www.smash­ing­magazine.com/2016/06/an-​introduction-to-redux/

PROOF OF CONCEPT

Here is the repo of our ini­tial proof of con­cept:

http://github.com/beno­rama/ngrx-​realtime-app

The repo is di­vided into two sep­a­rate projects:

  • Vert.x server app, based on Vert.x (ver­sion 3.3), man­aged by Gra­dle, with a main ver­ti­cle de­vel­oped in Groovy lang.
  • An­gu­lar client app, based on An­gu­lar (ver­sion 4.0.1), man­aged by An­gu­lar CLI with state, re­duc­ers and ac­tions logic based on @ngrx/store (ver­sion 2.2.1)

For the demo, we are using the counter ex­am­ple code (ac­tions and re­duc­ers) from @ngrx/store.

The counter client busi­ness logic is based on:

  • CounterState in­ter­face, counter state model,
  • counterReducer re­ducer, counter state man­age­ment based on dis­patched ac­tions, and
  • In­cre­ment, decre­ment and reset counter ac­tions.

State is main­tained server-​side with a sim­ple sin­gle­ton CounterService.

class CounterService {
    static INCREMENT = '[Counter] Increment'
    static DECREMENT = '[Counter] Decrement'
    static RESET = '[Counter] Reset'
    int total = 0
    void handleEvent(event) {
        switch(event.type) {
            case INCREMENT:
                total++
                break
            case DECREMENT:
                total--
                break
            case RESET:
                total = 0
                break
        }
    }
}

Client state initialization through Request/Response

Ini­tial state is ini­tial­ized with sim­ple re­quest/re­sponse (or send/reply) on the event bus. Once the client is con­nected, it sends a re­quest to the event bus at the ad­dress counter::total. The server replies di­rectly with the value of CounterService total and the client dis­patches lo­cally a reset ac­tion with the total value from the reply.

Vertx Request Response

Source: https://www.slideshare.net/Red­Hat­De­vel­op­ers/vertx-​microservices-were-never-so-easy-clement-escoffier

Here is an ex­tract of the cor­re­spond­ing code (from AppEventBusService):

initializeCounter() {
    this.eventBusService.send('counter::total', body, (error, message) => {
    // Handle reply
    if (message && message.body) {
            let localAction = new CounterActions.ResetAction();
            localAction.payload = message.body; // Total value
            this.store.dispatch(localAction);
        }
    });
}

Actions distribution through Publish/Subscribe

Ac­tion dis­tri­b­u­tion/sync uses the pub­lish/sub­scribe pat­tern.

Counter ac­tions are pub­lished from the client to the event bus at the ad­dress counter::ac­tions.

Any client that have sub­scribed to counter::ac­tions ad­dress will re­ceive the ac­tions and re­dis­patch them lo­cally to im­pact app states/re­duc­ers.

Vertx Publish Subscribe

Source: https://www.slideshare.net/Red­Hat­De­vel­op­ers/vertx-​microservices-were-never-so-easy-clement-escoffier

Here is an ex­tract of the cor­re­spond­ing code (from AppEventBusService):

publishAction(action: RemoteAction) {
    if (action.publishedByUser) {
        console.error("This action has already been published");
        return;
    }
    action.publishedByUser = this.currentUser;
    this.eventBusService.publish(action.eventBusAddress, action);
}
subscribeToActions(eventBusAddress: string) {
    this.eventBusService.registerHandler(eventBusAddress, (error, message) => {
        // Handle message from subscription
        if (message.body.publishedByUser === this.currentUser) {
            // Ignore action sent by current manager
            return;
        }
        let localAction = message.body;
        this.store.dispatch(localAction);
    });
}

The event bus pub­lish­ing logic is achieved through a sim­ple Ngrx Ef­fects. Any ac­tions that ex­tend RemoteAction class will be pub­lished to the event bus.

@Injectable()
export class AppEventBusEffects {

    constructor(private actions$: Actions, private appEventBusService: AppEventBusService) {}
    // Listen to all actions and publish remote actions to account event bus
    @Effect({dispatch: false}) remoteAction$ = this.actions$
        .filter(action => action instanceof RemoteAction && action.publishedByUser == undefined)
        .do((action: RemoteAction) => {
            this.appEventBusService.publishAction(action);
        });

    @Effect({dispatch: false}) login$ = this.actions$
        .ofType(UserActionTypes.LOGIN)
        .do(() => {
            this.appEventBusService.connect();
        });
}

You can see all of this in ac­tion by lo­cally launch­ing the server and the client app in two sep­a­rate browser win­dows.

Demo app screen

Bonus: the demo app also in­cludes user sta­tus (of­fline/on­line), based of the event bus con­nec­tion sta­tus.

The counter state is shared and syn­chro­nized be­tween con­nected clients and each local ac­tion is dis­trib­uted in real-​time to other clients.

Mis­sion ac­com­plished!

Typescript version of Vertx EventBus Client

The app uses our own Type­script ver­sion of the of­fi­cial JS Vertx Event­Bus Client. It can be found here, any feed­back, im­prove­ment sug­ges­tions are wel­come!

Next post

Time scheduling with Chime

Eclipse Vert.x executes periodic and delayed actions with periodic and one-shot timers. Chime is a time scheduler verticle that works on the Vert.x event bus.

Read more
Previous post

Dynamic Routing in Serverless Microservice with Vert.x Event Bus

The Serverless Framework has become the de facto toolkit for building and deploying serverless functions or applications.

Read more
Related posts

Real-time bidding with Websockets and Vert.x

The expectations of users for interactivity with web applications have changed over the past few years. Users during bidding in auction no longer want to press the refresh button.

Read more

Easy SSO for Vert.x with Keycloak

In this blog post, you'll learn how to implement Single Sign-on with OpenID Connect and how to use Keycloak together with Eclipse Vert.x.

Read more

Eclipse Vert.x metrics now with Micrometer.io

The new vertx-micrometer-metrics module provides support for collecting metrics with Micrometer.io and storing them in backends such as Graphite or InfluxDB.

Read more