Reactive programming and mvc

Reactive programming along with mvc

Immersive programming demos are impressive. Two of my favorites are:

It is surprising that such succinct code can accomplish such cluttered tasks with few abstractions. However, if the elegance combines with unfamiliarity, it’s easy to overlook that intellect we have discovered through hard years on the front lines of app development:

  • Parts Have to Be composable, decoupled, and testable
  • isolate your program state
  • don’t replicate yourself

Reactive programming should be along with the design patterns we all know by mvc. While those small demos seem to imply that immersive programming supplants mvc, the two are quite different. Reactive programming proposes that a small set of primitives for handling state with information flow, whereas mvc separates program issues.

In a fantastic mvc app the condition is from the version, so let’s examine how we could utilize reactive programming to your “M” and keep that different from your “V”.

spaghetti

We’ll start with an example of a simon-says game, executed in , but without any separation of issues.

The game will show the user a sequence of numbers and a set of buttons that are numbered. The participant pushes the buttons at the order of the figures. They acquire if they get them correct, otherwise it is a reduction. They could restart the game at any time. Here is a good example:

I know, I am horrible at making games.

The code are available in , but I’ve pulled the relevant portion below. It reads top to base, therefore I’ve just clarified the code inline.

// this is a flow of click events
Var newGameClicks = Rx.Observable.fromEvent($newGameButton, 'click on');

// this ends up being a flow of game outcome, which emits each time a game
// completes, but the road to get there's somewhat complex, so it is clarified
// below
newGameClicks
  // create an array of numbers on each click
  .map(arrayOfRandomNumbers)

  // use the array of numbers since the information to leave the game and pass it through
  .do(renderGameAndHideResult)

  .map(function(order) 
    // for each array of numbers, go back an event flow of button-clicks
    yield Rx.Observable.fromEvent($game.find('.number-buttons'), 'click on')

      // on every click, provided that the former state, we pass along the new
      // 'gameState', which comprises 2 peices of information:
      //
      // - the original order
      // - that the switches pressed far
      //
      // so after this call we get a flow of game-states which elicits each
      // time that the user clicks a few button
      .scan(order: order, pushed : [], work(state, event) 
        var val = +(event.target).val();
        return 
          order: state.order,
          pushed: state.pressed.concat([val])
        ;
      )

      // we've got 2 conclusion conditions:
      //
      // - incorrect button press
      // - that the participant got them correct
      //
      // therefore we make the flow of game-states finish on either of the preceding
      // conditions.  Note that this uses a helper function characterized in lib/common. Js
      // that acts exactly the same as RxJS's 'takeWhile', but it includes the last
      // thing.
       .takeWhileInclusive(function(state) 
        var prefix = state.order.slice(0, state.pressed.length);
        return _. IsEqual(prefix, state.pressed);
      )
      .take(order.length)

      // we only want to bother ourselves with the state of objects when the
      // game finishes, so this yields a flow of only 1 game-state, which
      // dissipates on the click
      .last()

      // finish the flow if the user requests a brand new game.  This still
      // yields a flow of only 1 game-state
      .takeUntil(newGameClicks);
  )

  // now we have got a flow of streams of solitary game-states, comparable to some
  // nested variety of objects '[[{}], [{}], ...]'.  We really only want a
  // optimally flow of the final game-states, so the phone below takes the
  // *internal* streams
  . ConcatAll()

  .do(renderResultAndHideGame)

  // at RxJS, those event streams aren't active if you telephone something like
  // subscribe or forEach
  .subscribe();

True to form this implements the match succinctly using common immersive programming idioms, all built on one abstraction: the event flow (or even Observer from RxJS parlance). It does not have the familiar looking class or view definitions we are utilised to seeing in popular mvc frameworks, but it’s got all those side-effect completely free functions… surely this code that would exude a nod of approval from functional purists, right? This code does not feel right though. It is mixing our application’s data model (the order of the figures, the results of the games) with displaying the information.

It is the jquery spaghetti of functional javascript.

What is wrong

We want to have the ability to launch this app without altering the preceding code, but it’s easy to consider features that will trip us up:

  • Adding keyboard shortcuts to get pushing the number buttons
  • reporting scores to some backend on wins/losses
  • introducing different ways to start a new sport, for instance by navigating via pushState

More commonly, there are questions we could ask as a litmus test of whether our UI parts are maintainable:

  • Will we create many views that act on the same data?
  • Are those views conducive to device testing?
  • Can we automate the control of this app out of the dev tools console?

The code above does not meet any of them.

The underlying information model

The underlying information model of this program could look something like this:

  • A flow of “new games”, each of which are represented by a Range of numbers that the player needs to fit
  • a flow of results, which include both the original order and the switches that the participant pressed

The new game flow can not only be hard-coded to emerge from one button clicks though. We need a degree of indirection at which any event flow can be connected or disconnected in the brand new game flow dynamically.

A decoupled strategy

To achieve this we will have to present a new abstraction, something RxJS calls for a Theme that consumes and generates events.

Var subject = new Rx.Subject();
Subject.subscribe(console.log);
subject.onNext('foo');
// => 'foo'

We must create arrays of arbitrary numbers, however, regardless of what is passed into the onNext telephone number.

Var subject = new Rx.Subject()
subject.map(createArrayOfRandomNumbers)
  . Register(console.log);
subject.onNext('foo');
// => [1, 3, 1, 0]

Now, however, the issue is how map yields a new observable, and we all need one thing that absorbs the onNext calls and also generates the arrays.

Var subject = new Rx.Subject();
Var observable = subject.map(createArrayOfRandomNumbers);

var newGameStream = Rx.Subject.create(subject(visible);
newGameStream.subscribe(console.log);
newGameStream.onNext();
// => [2, 2, 0, 3]

There’s one more subtlety. The observable factor will generate a new value for every subscription, perhaps not onNext call. Which means different observers would acquire unique arrays of worth, defeating the purpose of using a Theme at the first location.

newGameStream.subscribe(console.log);
NewGameStream.subscribe(console.log.bind(console, 'next subscription:'-RRB-);
newGameStream.onNext();
// => [ 0, 1, 3, 2]
// => next subscription: [3, 1, 0, 1]

In instances where the behaviour is deterministic, this would be safer, as it insulates us from inadvertently sharing information. However in this case we want every subscription to receive exactly the identical price, therefore we’ll share the Observable.

Var subject = new Rx.Subject();
Var observable = subject.map(createArrayOfRandomNumbers)
  .share();
var newGameStream = Rx.Subject.create(subject(visible);
newGameStream.subscribe(console.log);
newGameStream.subscribe(console.log.bind(console, 'next subscription:'-RRB-);
newGameStream.onNext();
// => [2, 1, 0, 3]
// => next subscription: [2, 1, 0, 3]

A similar approach can be placed on the flow of results, permitting us to decouple the reactive data version from the views.

Tight views combined loosely

This layout enables simple views accountable for nothing beyond converting the information to DOM. The views are modular and testable, and the version does not have to be altered for each new attribute. We’re reaping the advantages of reactive programming while maintaining mvc’s separation of issues.

: there are various flavors of mvc, and this guide is chiefly concerned about the view and model, therefore I am utilizing “mvc” as a generic term.

: the idea for the sport comes directly from @lawnsea’s talk, which was kinda the inspiration for the post.

© aaron stacy 2014, all rights reserved