Skip to content
pekman edited this page Mar 20, 2012 · 41 revisions

AaltoApps architecture

High level server architecture

AaltoApps runs in Amazon EC2 cloud server instance running Ubuntu Linux 11.10. The server instance uses EBS data storage for the files. Usually an EC2 instance loses all it's data when shut down with no disk space. EBS storage costs a bit more but is required to secure the data (logs, application files, configurations) if the instance is shut down. The project is written using Ruby language and Ruby on Rails web framework. The Ubuntu instance is modified by installing a HTTP server (Nginx), Rails application server (Passenger), Ruby interpreter (ruby 1.9.2), Rails framework (Rails 3.1.1), a database server (PostgreSQL 9.1) and a set of required libraries. Database and application files are saved in a separate EBS file system for easier backup possibilities and to be able to run the database in a separate server in the future.

The login process and user data is handled by ASI service running outside the main application. AaltoApps uses REST (Representational State Transfer) calls to ASI service for authentication and saves only the ASI id in it's own database, so the software can link user to his/her projects, comments and ratings.

The overall high level architecture is described in the following image:

Server instance architecture

The server has a static IP address and it receives HTTP and HTTPS requests from the client. The server uses Nginx web server as a HTTP(S) server which translates the requests for Passenger application server. Passenger handles the Rails instances which can handle only one request at a time. If all the application stacks are busy Passenger launches more instances.

The Rails stack is almost a pure MVC (model-view-controller) application. The requests are captured by a router which breaks them for controllers. E.g. http://domain.com/products/delete/123 routes by default to a controller named "ProductsController" and to an action method named "Delete". The last number represents an id, so the call deletes a product with an id "123".

The controllers are backed up by models which are saved to a database storage. A model can be e.g. a user, a product or something more abstract like a platform or category. Ruby on Rails uses an ORM (object-relational mapper) called ActiveRecord and it transforms the database data and relations to objects to be used by the controllers and views. So the controllers gather and prepare the models as instance variables which are shown in the views.

The views are for representing the data. In AaltoApps the views are mainly HTML5 and CSS3, although they can be anything like JavaScript or HAML syntax. A view gets it's data variables from controllers and render it for the user.

So in summary, user gets an access to the controllers. Controllers get the data as models and display it in views.

The insides of an EC2 instance:

AaltoApps Controllers

AaltoApps contains the following controllers:

ApplicationController

All the controllers inherit the ApplicationController. Contains the getter and setter for current_user and authentication filters to be used in all controllers.

ProductsController

Lists, shows, creates, updates and deletes products added by users. Ability to request approval and approve or block products.

index - lists products

GET: /products?parameters

HTTP GET parameters:

  • page (for paging the products, handled automatically by views)
  • platform (id of a platform, filters the products)
  • tags (comma-separated list of tags, filters the products)
  • sort (how the products should be sorted: popularity, created_at, avg_rating, featured)
  • q (query search string for product name)
  • myapps (show user's own applications)
  • approval (show applications with specified approval status (pending, published, blocked)

new - renders a page to create a new product, requires login

GET: /products/new

create - creates a product, requires login

POST: /products

Required HTTP POST parameters:

  • category
  • name (length > 3 characters)
  • description
  • url (length > 12 characters)
  • platforms

show - displays a specific product

GET: /products/ID, ID = product id as an integer

edit - opens an edit view for an existing product, requires login

GET: /products/ID/edit, ID = product id as an integer

update - updates an existing product, requires login

PUT: /products/ID, ID = product id as an integer

destroy - destroys an existing product, requires login

DELETE: /products/ID, ID = product id as an integer

promote - make the product featured, requires admin login

GET: /products/ID/promote, ID = product id as an integer

demote - clear the product's featured flag, requires admin login

GET: /products/ID/demote, ID = product id as an integer

approve - approves the product, requires admin login

GET: /products/ID/approve, ID = product id as an integer

block - blocks the product, requires admin login

GET: /products/ID/block, ID = product id as an integer

request_approval - requests approval for the product, requires login as the product's owner

GET: /products/ID/request_approval, ID = product id as an integer

autocomplete_tags - AJAX auto-complete handler for tag names

GET: /products/autocomplete_tags?term=PREFIX, PREFIX = tag prefix

Returns a JSON list of tag names matching the given prefix.

DownloadsController

Deletes downloads.

destroy - deletes an existing download

DELETE: /products/P_ID/downloads/DL_ID, P_ID = product id as an integer, DL_ID = download id as an integer

UsersController

Lists, shows, creates, updates and deletes users in the system.

index - lists users in the system

GET: /users

show - shows a specific user

GET: /users/ID, ID = user id as an integer

new - form for creating a new user

GET: /users/new

create - creates a new user to the system

POST: /users

Notable parameters:

  • user[term] should be 1 (accept terms and conditions)

edit - form for updating an user

GET: /users/ID/edit, ID = user id as an integer

update - updates an existing user

PUT: /users/ID, ID = user id as an integer

destroy - destroys an existing user

DELETE: /users/ID, ID = user id as an integer

SessionsController

Login and logout.

index - form for login

GET: /sessions

create - login to the system

POST: /sessions

HTTP POST parameters:

  • username
  • password

destroy - logout from the system

DELETE: /sessions

RatingsController

Product rating.

create - creates a rating for a product

POST: /product/ID/ratings, ID = id of a product as an integer

HTTP POST parameters:

  • rating (0..5)

PlatformsController

Lists, shows, creates, updates and deletes platforms in the system.

index - lists all platforms in the system

GET: /platforms

new - form for creating a new platform

GET: /platforms/new

show - displays a platform in the system

GET: /platforms/ID, ID = id of a platform as an integer

edit - form for updating a platform in the system

GET: /platforms/ID/edit, ID = id of a platform as an integer

create - creates a new platform

POST: /platforms

HTTP POST parameters:

  • name
  • image_url

update - updates an existing platform in the system

PUT: /platforms/ID, ID = id of a platform as an integer

HTTP PUT parameters:

  • name
  • image_url

destroy - deletes a platform in the system

DELETE: /platforms/ID, ID = id of a platform as an integer

add_product - adds a product to a platform

CategoriesController

Lists, shows, creates, updates and deletes categories in the system.

index - lists all categories in the system

GET: /categories

new - form for creating a new category

GET: /categories/new

show - displays a category in the system

GET: /categories/ID, ID = id of a category as an integer

edit - form for updating a category in the system

GET: /categories/ID/edit, ID = id of a category as an integer

create - creates a new category

POST: /categories

HTTP POST parameters:

  • name
  • image_url

update - updates an existing category in the system

PUT: /categories/ID, ID = id of a category as an integer

HTTP PUT parameters:

  • name
  • image_url

destroy - deletes a category in the system

DELETE: /categories/ID, ID = id of a category as an integer

CommentsController

Creates comments in the system.

create - creates a comment to a product

POST: /products/ID/comments, ID = id of a product as an integer

HTTP POST parameters:

  • body

update - updates an existing comment

PUT: /comments/ID, ID = id of the comment

HTTP PUT parameters:

  • body

destroy - deletes a comment

DELETE: /comments/ID, ID = id of the comment

PagesController

Shows static or semi-static pages. The pages are rendered from template files in the controller's view directory.

show - shows a specific page

GET: /pages/ID, ID = page id (template file name without path and extensions)

AaltoApps Models

Models are saved and read from the database through ActiveRecord object-relational mapper. The validations and relations are handled and abstracted in the ActiveRecord stack instead of the database. It is easy to change the database adapter to a NoSQL one from relational database with this approach.

The database schema for the models is created through migrations. Every time we create a model we also create a migration which maps the model attributes to the database, adds indexes etc.

AaltoApps has the following models:

Product

Has one:

  • Publisher (User)
  • Category

Has many:

Validates:

  • The presence of name, description, url and category
  • The length of name is at least 3 characters
  • The length of url is at least 12 characters
  • Approval state in (pending, published, blocked)
  • Photo attachment size is under 5 megabytes
  • Photo content type is jpeg or png image

Category

Has many:

  • Products

Validates:

  • The presence and uniqueness of name
  • The length of name is at least 3 characters

Platform

Has many:

  • Products

Validates:

  • The presence and uniqueness of name
  • The length of name is at least 3 characters

User

Has many:

  • Ratings
  • Published (Products)
  • Comments

Validates:

  • The presence of ASI ID

Rating

Has one:

  • User
  • Product

Validates:

  • The presence of rating, user and product
  • The rating is a float between 1 and 5, step is 0.5

Comment

Has one:

  • Product
  • Commenter (User)

Validates:

  • The presence of commenter and product
  • The length of message body is at least 3 characters.

Download

Has one:

  • Product

Validates:

  • The presence of attached file and title

Session

Handles the communication with ASI server API. Stores the cookie and handles the UserConnections. Login and logout.

UserConnection

The actual REST calls are sent from here.

MarkdownHelper

Defines helper functions for processing Markdown-formatted fields in models.

Ability

CanCan library's helper model for user abilities.

Views

The views are generated and handled by the controllers. Each action usually has their own view template. The view is constructed from a layout template with a header and a footer. Every GET action renders a template to the middle part of the layout.

Partials

It is not recommended to repeat yourself in the views. Some repeating stuff should be handled as a partials, which are quite like sub-templates that get parameters and render stuff to the actual templates.

Helpers

Sometimes we need helper functions in the views. Those should be written as helpers which are only available in the views.