The Fairway Technologies Blog

blog

Slapstick - a Backbone Tutorial, Part 1 - Modeling the Model

In backbone.js, Blog, JavaScript, Rails 3.2, Ruby, software development No Comments

Introduction

It's time to get going on the Slapstick application, 'cause it isn't going to build itself.  Before we jazz up our application with all kinds of swanky JavaScript goodness, we need a foundation to build on.  In this post, we'll talk about the objects we'll be working with to represent our fantasy hockey team.

The Model

Right, what were we doing again?  Oh yeah - I remember!  We're talking about the domain model that we'll be working with for our fantasy hockey application.  Here's what we'll need to represent:

  • Team:  A Team will represent our user's collection of Players.  Nothing exceptionally fancy here - if the user's Team didn't have a name, we might be able to get away without having a Team object - we could just pass around a collection of Skaters or Goaltenders.
    • Were we building a full-fledged fantasy hockey app, we'd probably tie a Team to some sort of User model.  We're not, so you'll just have to use your imagination.
  • Position:  Represents what the Skater's role on the ice is.
  • Skater:  A Skater is any player on a hockey team except the Goaltender.  They shoot the puck at the Goaltender and try to prevent other Skaters from shooting by tickling the Skaters on the opposing team.
    • In this context, "tickling" means throwing one's full weight headlong into the opponent.  Preferably into a wall.
  • Goaltender:  The player who... err... tends the goal.  He tries like hell to prevent the Skaters from scoring points.  And looks the coolest.

The interesting thing about a Skater and a Goaltender is that they share certain attributes.  Skaters and Goaltenders both have names, numbers and number of games played.  Beyond that, a Skater has different stats than a Goaltender.  For example, a Skater has stats like goals, shots, plus/minus, etc.  A Goaltender, on the other hand, has stats like goals against, shots against, saves, wins, losses, etc.  To see a full list of the stats we'll be tracking for a Skater and Goaltender, you can check out the models here.

Modeling the Skater and the Goaltender

Okey doke, we've established that the Skater and Goaltender share some attributes but differ in others.  This is a pretty basic object-oriented problem to solve, right?  Right.  We'll add a Player class to our domain model.  The Player class will have all the attributes that are common to both Skaters and Goaltenders.  We'll use a module (the TeamMember module, discussed below) to make the Player attributes transparent when we're interacting with the Skater or Goaltender (more on that below, too)  So we'll have something that looks like this (I'm not a UML genius, but hopefully this conveys the idea):

UML for Player, Skater, Goaltender classes and TeamMember module

That's all fine and dandy, but we have a bit of an issue.  In a Rails application, the models are tied pretty closely to the database.  To get technical, Rails applications seem to adhere pretty strictly to the Active Record pattern.  That means that our models also know how to persist themselves.  But how does database persistence work with a scheme like what we have above?  Our Skater and Goaltender both interact with Player via the TeamMember module, meaning we have a single record for a fantasy hockey player in two tables.  So how do we represent that in the database?  As it turns out, we have a couple of options:

Single Table Inheritance

Out of the box, ActiveRecord (the de facto ORM for Rails applications, thus the one we'll be using in Slapstick) supports single table inheritance (scroll down about 75%).  Done deal, right?  Well, not so fast.  This sentence from ActiveRecord's documentation catches my eye and causes a disturbance in the Force:

Note, all the attributes for all the cases are kept in the same table.

That means that our database would have a giant Players table with all of the attributes we care about - Player, Skater and Goaltender.  So what?  I'll tell you so what - if we save a Skater to this table, all of the Goaltender columns will be null.  Same deal for a Goaltender - all of the Skater columns would be null.  Before we know it, we'd have a table that looks like swiss cheese - nulls everywhere!  That reminds me of something the late great comedian Mitch Hedberg said about swiss cheese:  Swiss cheese is the only cheese you can bite into and miss.

Devil's Advocate

Now, let's not kid ourselves.  Here are some questions/comments that might be running through your head:

  1. Would single table inheritance work for our demo?  It seems simple enough, and should be totally sufficient for a demo application.
  2. How deep could the Players table possibly get?  1,000 rows?  I'd be surprised if a fantasy hockey app had 15,000 records in the Players table.
  3. Considering question 2, there won't be any real performance hit - who cares if the table has some nulls in it?
  4. The table might be a little wide with all those columns, but our application won't care.  ActiveRecord will merrily fetch our data and map it to our object - signed, sealed and delivered.

Devil's Devil's Advocate

A few things (in no particular order):

  1. My inner DBA cringes at the thought of a 50-column table that will have a whole bunch of nulls in it.
  2. I tend to avoid null values in my database when I can.  I can't really explain it; it's something I've always done.  There are plenty of arguments for both sides (to null or not to null), and they're both right.  Good for them.
  3. It's been my experience that people (e.g. report writers or data analysts) will always look at your database outside of your application.  They'll have to know when columns will be null for certain types of records.
  4. I'm lactose intolerant (my friends call me a "lactard"), so swiss cheese can suck it.

Multi-Table Inheritance

I think it'd be ideal to store the Skater and Goaltender data in separate tables, with an associated record in a Players table to store the common attributes.  Here, maybe a picture will help:

 

Database relationship diagram between players, skaters and goaltenders. The intent of this diagram is to show that a players record will be associated to either one skaters OR goaltenders record, but not both.

 

Yeah, I like that a lot better.  Want to know stuff about a Skater?  Go to the skaters table.  Need to find some stats on a Goaltender?  No problem, the goaltenders table has got you covered.  Need some additional data about whoever you're looking up?  A quick join to the players table will get you whatever you need.

Now, to get this to actually work, we'll need some of Ruby's module magic.  There's a really great article on multiple table inheritance and how to implement it in a Rails app.  Rather than regurgitate that article here, I encourage you to follow the link and read it.  Suffice to say we've leveraged what we learned from that article and added a TeamMember module.

The TeamMember module does a couple cool things for us.

Polymorphic Associations

Sounds cool right?  It's pretty neat.  Basically, we can tell ActiveRecord to treat a Skater or Goaltender like a Player.  We give the association an alias of sorts - I've chosen the name TeamMember, 'cause that's pretty much the same thing as a Player and Player is already taken.  Our polymorphic assocation looks like this:

%SLAPSTICK_DIR%/lib/team_member.rb:

has_one :player,
:as => :team_member,
:autosave => true,
:dependent => :destroy

This means the players table has two extra columns:

  • team_member_id:  This column records the skater.id or goaltender.id, depending on the type of player that's being stored.
  • team_member_type:  This column records the type of player being stored.  The values will be either "Skater" or "Goaltender".  This column tells ActiveRecord what class to use when building an object from the database.

This is really an all too brief discussion on polymorphic relationships (which is as hard to say sas it is to type, by the way).  You can read more about polymorphic associations here.

Attribute Access

Polymorphic associatins not enough for you? Fine. With the acts_as_team_member method, we can access the Player attributes directly from the Goaltender or Skater object.  Want to know what the Skater's name is?  No problem - you can use @skater.name instead of @skater.player.name. I don't know about you, but I think that's pretty slick.

As with all things in software development, there are a couple of trade-offs, along with some ideas on how to solve them:

  1. To fetch a "full" Skater or Goaltender (i.e. a Skater or Goaltender that also has his Player data), you'll need to execute an extra query - one against the skaters table (or goaltenders, depending on what you're looking for) and another against the players table.
    1. One way around this is to use a view that joins the players and skaters table and another that joins the players and goaltenders table.  Query the view, and you're all set.
    2. Another way around this is to join the skaters/goaltenders table to the players table in your query.  No view. No biggie.
    3. Finally, with connection pooling and other optimizations that just about every database server makes, two rapid-fire queries aren't going to be very expensive, especially if the query is against indexed columns (which will be the case in our situation).
  2. It's not immediately obvious how the records in the goaltenders and/or skaters table are associated to records in the players table.
    1. The skater's id value is recorded as team_member_id in the players table.
    2. How would anyone else know that?  The easy solution - add a foreign key from the skaters table to the players table on team_member_id.  Same deal for the goaltenders table.  That way, the association between tables will be immediately obvious for anyone looking at the database schema.

What's Next?

I know the title says "Backbone Tutorial" that would focus on sexy, maintainable UI code, and we're getting there.  Honest.  In our next post, we'll start bringing Slapstick to life (and y'know, doing what we said we'd do).  We'll take a look at building a solid infrastructure for our UI.  Get ready folks, the skates are about to hit the ice - we'll have the Minas Tirith Marauders facing off against the Corellian Casual Fliers in no time!

Oh yeah - if you want to see the source code so far, you can find it here.

New Call-to-action

Sign Up For Our Monthly Newsletter

New Call-to-action