As mentioned in this post, we’ve been aligning our approach to business intelligence with our architectural principles. One of our hypotheses was that by taking an event-based approach to analytics we would be able to be more agile with our data. In order to test this hypothesis, we needed a ‘killer-app’ that would not only prove the approach, but also help us sell it to the rest of the business.
Finding a Killer-App
We sell our product via two primary channels: online and offline. Our offline channel is served by a contact centre in our Northampton office. Sales consultants call potential customers and attempt to make sales over the phone. Previously, whenever consultants made sales, they would have to get up from their desks, walk to their team’s white board, and manually record the sale.
Our idea for the ‘killer-app’ was to utilise the wall boards scattered about the contact centre for displaying a sales leaderboard fed by events from our back office system. Not only would this remove an inefficient manual step in the sales process, it would also foster a sense of friendly competition amongst consultants (we hoped).
The Leaderboard
The leaderboard is a simple ranking of consultants by the total value of the product that they have sold for the day. Rankings can be split out by teams to show consultants not only how they rank with the rest of the contact centre, but also within their own team. It looks something like this (note that this is sample data):
How it Works
Whenever a consultant completes a sale on our back office system, it publishes a sales event. The leaderboard application subscribes to these sales events. Once a sales event is consumed, it is immediately pushed up to the leaderboard.
The Sales Event
The event publish/subscribe is performed using RabbitFeed.
Publishing
The event publish is done at the point the consultant completes a sale in the back office. This event is then available to any downstream application for consumption.
The sales event is defined in the RabbitFeed initialiser like this:
EventDefinitions do
define_event('consultant_completes_sale', version: '0.0.1') do
defined_as do
%w{
A consultant has completed a sale in the back office.
}
end
payload_contains do
field('consultant', type: 'string', definition: 'The name of the consultant')
field('team', type: 'string', definition: 'The name of the team to which the consultant belongs')
field('amount', type: 'string', definition: 'The amount of the sale, in dollars')
end
end
end
The event is published from the Rails controller:
class SalesController < ApplicationController
def create
sale = Sale.new sale_params
if sale.save
RabbitFeed::Producer.publish_event 'consultant_completes_sale', {
consultant: sale.consultant.name,
team: sale.consultant.team,
amount: sale.amount,
}
...
else
...
end
end
end
Subscribing
A RabbitFeed consumer process runs in the Rails environment on the leaderboard server. This process will apply the sale to the day’s rankings and will push the update to the clients.
The subscription is configured in the RabbitFeed initialiser like this:
EventRouting do
accept_from('back office') do
event('consultant_completes_sale') do |event|
(EventHander.new event).update_leaderboard
end
end
end
When an event is consumed, a consultant ranking record is found or initialised and the sale is applied to the consultant’s ranking. The updated ranking is then pushed up to the clients. The event handling code looks like this:
class EventHandler
attr_reader :event
def initialize event
@event = event
end
def update_leaderboard
consultant_ranking.record_sale! event.amount
push_to_clients
end
private
def consultant_ranking
@consultant_ranking ||= ConsultantRanking.find_or_initialize_by(
consultant: event.consultant,
team: event.team,
date: (Time.parse event.transacted_at_utc).to_date,
)
end
def push_to_clients
...
end
end
Updating the Leaderboard
At any one time, there will be 10-15 clients displaying the leaderboard in a browser window. There are a few options available for updating the leaderboard with new data and sales. We could simply trigger the browser to refresh the page at specified time intervals, but this method doesn’t afford any interactivity or excitement when new data is displayed. Alternatively, we could have each client continuously poll the server for updates, but this requires the client to maintain some state about the updates it has processed. We wanted a mechanism by which all clients would be updated simultaneously and instantaneously whenever a sale was made. This allows the client to apply any updates to the leaderboard with a flash! We were able to achieve this via the use of Web Sockets.
Configuration
First, we need to add the websocket-rails
gem to our leaderboard application:
gem 'websocket-rails'
Next, we need to enable the RabbitFeed consumer process to trigger websocket events. Because the RabbitFeed consumer process is asynchronous, it runs as a separate process outside of the leaderboard web application. We needed a means for websocket events triggered within the RabbitFeed consumer process to be sent to clients connected to the leaderboard web application. Fortunately, websocket-rails
has this capability built-in: Using Redis, a websocket event triggered on one process will be pushed onto Redis and synchronised to the other process.
Set this configuration option in the websocket-rails initialiser to enable websocket synchronisation:
# Change to true to enable channel synchronization between
# multiple server instances.
# * Requires Redis.
config.synchronize = true
The websocket event will be published to the consultant_ranking
channel. The clients will subscribe to this channel. The event is published by calling trigger
with the change
event and a payload. The client handler will bind to this event type. The code to trigger the websocket event looks like this:
class EventHandler
...
def push_to_clients
WebsocketRails[:consultant_ranking].trigger :change, {
consultant: consultant_ranking.consultant,
team: consultant_ranking.team,
units_sold: consultant_ranking.units_sold,
sales_revenue: consultant_ranking.sales_revenue,
ranking: consultant_ranking.ranking,
}
end
end
On the client side, we create a websocket pointing back to the websocket URL on our server. We then subscribe to the consultant_ranking
channel and define a handler function for change
events. The event handler will be called for every change event triggered on the server. The event handler will apply the change in ranking to the leaderboard with a flash. The code looks like this:
// Create the socket
// The socket URL looks something like this: http://localhost:3000/websocket
var dispatcher = new WebSocketRails(window.location.host+'/websocket');
// Subscribe to the websocket channel on which we publish the events
var channel = dispatcher.subscribe('consultant_ranking');
// Declare the event handler
// Bind to the 'change' channel event
channel.bind('change', function(consultant_ranking) {
// This is what gets called when an event is received
update_consultant_ranking(consultant_ranking);
};
Performance
Due to the distributed nature of the design, there are no bottlenecks between the back office application and the leaderboard. As soon as a sales event is published, the leaderboard subscriber consumes the event and pushes it up to the clients. In fact, the sale often registers on the leaderboard before the back office system can load the post-sale page!
Outcome
The leaderboard was an instant success: Sales consultants began vying for the top ranking on the leaderboard, team managers were able to get immediate feedback on the performance of their team members, and senior management gained improved insight on who the top performers in the contact centre were. The leaderboard proved that an event-based approach to analytics would allow us to be more agile with our data. Additional applications were soon built using the same approach. For example, we created a daily sales dashboard showing stats on the day’s sales, split by the online and offline channels as well as a business KPI monitoring and alerting system.
Find out more about how you can use RabbitFeed to unlock the potential of your data
Ready to start your career at Simply Business?
Want to know more about what it’s like to work in tech at Simply Business? Read about our approach to tech, then check out our current vacancies.
This block is configured using JavaScript. A preview is not available in the editor.