Implementing an end-to-end reactive application

Matthew de Nobrega
5 min readOct 29, 2018

--

In the previous article in this series I looked at reasons to build web and mobile applications on top of a high-quality event history. In this article I cover a proof-of-concept (API and Web) built with .NET Core and Angular, and leveraging xAPI, ReactiveX, SignalR, Azure event grid, Azure functions, and CosmosDB.

This excellent series covers similar thinking from an enterprise perspective.

Design goals

  • A consistent way of handling events — from UI through to persistence, and from transport to business logic
  • A simple mechanism for stitching chains of causes and effects together
  • Staying as true as possible to the principles of DDD and the Reactive manifesto, while minimizing complexity and maximizing developer productivity

Design decisions

  • Use xAPI statements as a standard format for all events
  • Use web sockets for HTTP transport, and implement adapters on either side of the network to create continuity between the client and server event channels
  • Use RX for the event channels on both client and server — on the server provide a mechanism to bind business logic in-process to the channel (ie run as a monolith) or bridge the channels to Azure event grid topics (in which case the business logic runs in Azure functions, ie serverless)

Scope

The proof-of-concept is a simple web form that allows users to create and update people with names. The scope was intentionally kept small while highlighting the differentiating aspects of the approach.

Flow through the application

The terminology is modified from the perception-action cycle. The flow for asking a (human) colleague to get lunch would look like the following:

In a web context, the interaction with the outside world and the lower levels of perception and action are handled in physical hardware, the operating system, the browser, and the UI framework. Zooming in on just the web application we have:

The responsibilities of the UI are to:

  • Transform UI interactions into perception statements that capture domain-specific meaning
  • Update the UI model based on decision statements, which contain relevant state as part of their payload

The perception and decision channels transport xAPI statements between the client and the business logic on the server. The client channels have the same events as the server channels, but filtered down for just the current user.

For this scope the responsibilities of the API are to:

  • Make decisions — ie transform perception statements into decision statements
  • Work through the repercussions of decisions — ie transform decision statements into more decision statements (the feedback loop in the diagram above)
  • Manage the persistence and retrieval of all perception and decision statements

There is more information on how the solution is structured and deployed in the API README, and a detailed run-through of an example flow as a postscript.

This looks like REST with pseudy-psychological terminology?

The key differentiating points compared to REST or GraphQL are:

  • Everything is asynchronous — there is no impedance mismatch between a request-response cycle and reactive plumbing
  • Decisions are broadcast — not hidden in private responses — so any logic that depends on a decision has access to it
  • Every decision can be traced back to its initiating perception, and conversely every decision resulting from a perception can be traced
  • The transport model is the persisted data model is the audit log

Use cases — this approach is appropriate where:

  • There is a lot of user interaction, and the interactions themselves are of interest
  • The data sent to users is personalized (response caching is better supported by REST) and relatively simple (GraphQL is better for complex queries)

Thinking ahead

The aim of the proof-of-concept was to demonstrate a practical way to implement an end-to-end reactive web application on top of a high-quality event history. In addition the solution demonstrates a method to support both monolithic and distributed serverless deployments for such an application.

Many areas are not covered —for example authentication, efficient querying, composition of multiple bounded contexts, and fault tolerance — but my reading is that for appropriate use cases the approach is sound, and these areas will not present insurmountable issues.

Postscript — detailed information flow

Saving a person when running as a distributed application:

  • Sensing and the early phases of perception happen outside the application, going from keyboard and mouse, through the hardware, operating system, browser, and UI framework
  • The UI application transforms UI framework (in this case Angular) events into xAPI perception statements that capture domain-specific meaning — for example transforming the submit button click event into a statement that the user is requesting the creation of a person with a certain name
  • These perception statements are put onto the perception channel, which is an RX Observable in the UI
  • The statement flows from the UI perception channel, through a web socket connection, into the server perception channel, which is also an RX observable, and is then forwarded onto the event grid perception topic
  • An Azure function picks up the statement, and hands it with any needed dependencies to the Person business logic for a decision
  • The Person business logic persists the perception statement, and transforms it and the dependencies into a decision statement (in this case saying that a person was created), which includes the state of the person and is linked back to the precursor perception statement
  • The decision is persisted and put on the decision event grid topic
  • The statement flows into the server decision channel, and through a web socket into the UI decision channel
  • The UI acts on the decision — in this case by updating the UI model with the returned data
  • As with perception, acting is a multi-stage process going through the browser (in this case triggering a repaint), through the computer and out to the screen — the ‘outside world’ implication of the entire process is a change to the pixels on the screen

--

--