Building Your Own vs. Using a SaaS: Choosing the Right Voting System

Struggling to decide between building your own voting system or using a SaaS solution? This guide explores the pros and cons of each approach, with a basic Ruby on Rails implementation for a DIY (do-it-yourself) voting system.
Building Your Own vs. Using a SaaS: Choosing the Right Voting System

When creating a website that needs voting functionality, you have two main options:

  • Design your own voting system
  • Use a Voting Software as a Service (SaaS)

Let's compare these options.

Designing Your Own Voting System

Advantages

  • Full control over features and design
  • Can customize to fit your exact needs
  • No ongoing subscription costs
  • Data stays on your own servers

Disadvantages

  • Takes time and effort to develop
  • Requires technical skills
  • You're responsible for security and maintenance
  • May lack advanced features of professional solutions

Using a Voting SaaS

Advantages

  • Quick and easy to set up
  • Professional features already included
  • Security handled by experts
  • Regular updates and improvements

Disadvantages

  • Monthly or yearly costs
  • Less control over design and features
  • Dependent on third-party service
  • May have limits on number of votes or users

Designing your own voting system

Basic Schema

Users
-----
- id (Primary Key)
- username
- password_digest
- created_at

Polls
-----
- id (Primary Key)
- title
- description
- created_by (Foreign Key -> Users.id)
- created_at
- ends_at

Options
-------
- id (Primary Key)
- poll_id (Foreign Key -> Polls.id)
- content

Votes
-----
- id (Primary Key)
- user_id (Foreign Key -> Users.id)
- option_id (Foreign Key -> Options.id)
- created_at

Key points about this schema:

  • The users table stores user information.
  • The polls table contains information about each poll.
  • The options table lists the voting options for each poll.
  • The votes table records each vote, linking users to their chosen options.
  • The UNIQUE(user_id, option_id) constraint in the votes table prevents duplicate voting.

This schema allows for:

  • Multiple polls
  • Multiple options per poll
  • One vote per user per poll
  • Tracking when votes were cast

You could expand this schema to include more features like:

  • Poll categories
  • User roles (admin, regular user)
  • Poll visibility settings (public, private)
  • Vote weights (for weighted voting systems)

Relationships

  • A User can create multiple Polls (one-to-many)
  • A Poll has multiple Options (one-to-many)
  • A User can have multiple Votes (one-to-many)
  • An Option can have multiple Votes (one-to-many)
  • A Vote belongs to one User and one Option (many-to-one for both)

Key Features

  • Users can be uniquely identified and authenticated
  • Polls have a title, description, creator, creation time, and end time
  • Each Poll can have multiple voting Options
  • Votes are recorded for each User's choice of Option
  • The system can prevent duplicate voting by ensuring one Vote per User per Option

When designing your own system, focus on these key elements:

  • User authentication. Example: Create a simple login system where users register with a username and password. Before voting, they must log in to verify their identity.
  • Vote counting and storage. Example: Use a database table to store votes. Each row could contain: voter_id, option_voted_for, timestamp. To count votes, use a SQL query like: SELECT option_voted_for, COUNT(*) FROM votes GROUP BY option_voted_for.
  • Preventing duplicate votes. Example: Before recording a new vote, check if the user_id already exists in the votes table for this particular poll. If it does, don't allow another vote.
  • Displaying results. Example: Create a pie chart or bar graph to visually represent the voting results. You could use a library like https://www.chartjs.org/ to easily generate these graphics based on your vote counts.

Basic voting system using Ruby on Rails

Models

# app/models/user.rb
class User < ApplicationRecord
  has_many :votes
end

# app/models/poll.rb
class Poll < ApplicationRecord
  has_many :options
  has_many :votes, through: :options
end

# app/models/option.rb
class Option < ApplicationRecord
  belongs_to :poll
  has_many :votes
end

# app/models/vote.rb
class Vote < ApplicationRecord
  belongs_to :user
  belongs_to :option
end

Controllers

Set up routes in config/routes.rb:

Rails.application.routes.draw do
  resources :users, only: [:new, :create]
  resources :polls do
    resources :votes, only: [:create]
  end
end
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  helper_method :current_user

  def current_user
    @current_user ||= User.find(session[:user_id]) if session[:user_id]
  end
end

# app/controllers/users_controller.rb
class UsersController < ApplicationController
  def new
    @user = User.new
  end

  def create
    @user = User.new(user_params)
    if @user.save
      session[:user_id] = @user.id
      redirect_to polls_path
    else
      render :new
    end
  end

  private

  def user_params
    params.require(:user).permit(:username, :password)
  end
end

# app/controllers/votes_controller.rb
class VotesController < ApplicationController
  def create
    @poll = Poll.find(params[:poll_id])
    @option = @poll.options.find(params[:option_id])
    
    if !current_user.votes.exists?(option: @option)
      @vote = current_user.votes.create(option: @option)
      redirect_to @poll, notice: 'Vote recorded!'
    else
      redirect_to @poll, alert: 'You have already voted.'
    end
  end
end

Views

<!-- app/views/polls/show.html.erb -->
<h1><%= @poll.title %></h1>
<% @poll.options.each do |option| %>
  <p>
    <%= option.content %>
    <%= button_to 'Vote', poll_votes_path(@poll, option_id: option.id), method: :post %>
  </p>
<% end %>

<h2>Results:</h2>
<% @poll.options.each do |option| %>
  <p><%= option.content %>: <%= option.votes.count %> votes</p>
<% end %>

This is a basic implementation. You'd need to add more features like creating polls, handling edge cases, and improving the user interface. Remember to implement proper security measures, such as input validation and protection against CSRF attacks.