Sinatra Series: Case Study

Goal

  • We will build a large fairly complicated example
  • To illustrate more principles of Service Oriented Architectures and Sinatra

Non SOA Approach to scaling

  • A Web Server (aka App Server) runs your code.
  • That server is connected by network to a database server.
  • There are background processes and they run on the same server
  • Scaling it follows a path of least resistance
Scaling

  • As load becomes greater, capacity is added where it is needed
  • A “load balancer” sends traffic to different servers
  • A “cache service” like Redis reduces the number of requests to the database
  • And background processes are also replicated over multiple servers
  • This kind of configuration is typical of many sites
  • But what happens when the database cannot carry the load by itself?
  • Managing the monolithic code base itself also becomes a problem
  • Test suites take longer to run.

Case Study: Social Application

  • To delve more into SOA we will be using a “Social Application”
  • Here are the basics: you should be writing this app as you read
  • It will be helfpul with NanoTwitter

Features (long term)

This is an RSS feed reader, with a social network integrated into it. A user indicates that they are interested in a certain RSS feed, and they are imported into the app to allow the user to read it comfortably. Furthermore, users can follow each other so that user1 can follow along and see user2’s feeds (say they share an interest.) And if user1 sees a feed entry that they think is great they can vote it up . * User Profiles and Fllowing: basic profile information, user name, password, and who is following whom * Feeds: Each user can make feed entries, which are collected into their feed * Votes: Any user can vote up or down a feed entry, whether it’s one they are following themselves, or another user’s * Notifications: A user can request to be notified when someone votes on a feed item from one of their own feeds, when someone starts following them, and so on.

Overview

  • Users can follow other users
  • A user can follow 0 or more users, and be followed by zero or more users
  • A user cannot follow itself

Tables, in their initial form

  • users table - Columns
    • id: integer
    • name: string
    • email: string
    • bio: string
  • follows table - columns
    • id: integer
    • user_id: integer
    • followed_user_id: integer
  • feed table - columns
    • id: integer
    • user_id: user who owns the feed
  • feed_entry table - columns
    • id: integer
    • feed_id: feed to whom this entry belongs
    • body: text field contained
  • vote table - columns
    • id: integer
    • entry_id: integer
    • vote: string (up or down, room for expansion)

Models (not all are detailed here)

  • User model
    • Will contain some intricate has_manys
    • Along these lines
1class User < ActiveRecord::Base
2  has_many :follows
3  has_many :followed_users, ...
4  has_many :followings, ...
5  has_many :followers ...
6end
  • Follow model
  • Is a lot simpler structurally but really confusing remantically!. Here is one way to do it
1class Follow < ActiveRecord::Base
2  belongs_to :user
3  belongs_to :followed_user, class_name: "User"
4end

Testing

General
  • Because the follow/follower/following is very confusing
  • Write tests to see that they work as you want
  • Note that you need to ensure that the RACK_ENV environment variable is set to test
  • Create the test database with, e.g., rake db:drop db:migrate db:seed RACK_ENV=test
  • Invoke pry with the environment variable set: RACK_ENV=test pry (note fish shell is a little different)
  • Invoke pry with sinatra loaded: pry -e "require_relative ./app,rb`”
  • Use before do to set up “test fixtures”
Examples of additional tests to write
  • If user a follows user b,
    • then user b is a follower of a
    • also user a is a followed_user of user b
  • if user a is followed by b,c and d
    • then user a has three followers
    • also user b is a follower of a
    • user a is a followed_user if user a
  • If user a attempts to follow user a
    • then an error occurs
Sample tests
  • A very basic test to see if it works at all:
 1describe 'associations' do
 2  before do
 3    @p = User.create(name: "Pito")
 4    @cc = User.create(name: "Chris")
 5    @f = Follow.create(user: @p, followed_user_id: @cc.id)
 6  end
 7  it "can tell how many people are following me" do
 8    @cc.followings.length.must_equal 1
 9  end
10end

Paritioning into services

  • At a high level this service is pretty simple
  • There are just a few tables… but the semantics are tricky
  • But when we implement it properly we will touch on many of the representative challenges
  • As well as database/activerecord capabilitiers: caches, one-to-many, many-to-many, single-table-inheritance etc.

How to think about services

  • Fundamentally this is a designn challenge
  • There are different ways of attacking this
  • You can think of these questions:
    • Which information is read often but updated much more rarely? This will help determine which models can be in a different service
    • Which is written and updated frequently? You will want to possibly put the updating in a seperate service so that the main threads are not delayed
    • Which joins will occur the most often? You will want to keep those tables together in the same service.
    • Which parts of the system are clearly defined and have good requirements? If the requirements are still in flux you may not want to make them into a service but keep them as a conventional MVC app

Start here

  • It is natural to begin with a monolythic MVC design. This is true if you are implementing in Ruby/Sinatra, Ruby on Rails, Node or any other language.

  • Some or all the models could be broken off into services, one model per service, or more likely, several models in one service.

  • This is just a start. We will build on this scheme and use this in Nanotwitter

Deliverable

  • Build out the five tables and models
  • Do not design or implement the routing blocks or views
  • Do write unit tests, folllowing my example to see that your models and associations work
  • This is an individual assignment not a team assignment.
  • Include a readme explaining what you did and any interesting insights or obstacles you hit.
  • A zipped up sinatra app with tests according to the above suggestions
  • A readme explaining what is going on