-
CAP Theorem problem
- Partition tolerance will fail as a result of network connections
- This solution maximises for availability of data
- The assumption here is that the consistency of data is less important
-
Users need to be logged in to make changes
-
Sync server is responsible for reducing events
-
Two solutions proposed
- If no newsfeed or watching other users is required, a movie's favourites are only retrieved when a movie is read
- If watching other users is required, the solution is more fragile, and should probably result in a push from the server to the device to ensure a proper success response code (which isn't possible in the case of the server getting the results)
-
TODO Add in device tracking
- User A has 2 devices (D1 & D2).
- The devices have the following events on them
- D1
- E1 - { type: 'ADD', movie_imdb_id: 'aaaa', timestamp: 1472608060 }
- E2 - { type: 'DEL', movie_imdb_id: 'aaaa', timestamp: 1472608072 }
- D2
- E3 - { type: 'ADD', movie_imdb_id: 'aaaa', timestamp: 1472608064 }
- D1
- Syncing order is E1, E2, E3
- User A has 3 devices (D1, D2 & D3).
- The devices have the following events on them
- D1
- E3 - { type: 'ADD', movie_imdb_id: 'aaaa', timestamp: 1472607000 }
- D2
- E1 - { type: 'ADD', movie_imdb_id: 'bbbb', timestamp: 1472608000 }
- D3
- E2 - { type: 'ADD', movie_imdb_id: 'cccc', timestamp: 1472609000 }
- D1
- Syncing order is E1, E2, E3
- User
- Movies
- Favorite
- Event
- Device Solution 2
- UserDeviceEvent Solution 2
Relationships
- has_many :favorites
- has_many :movies, through: :favourites
- has_many :events
- has_many :devices Solution 2
Important Fields
field name | type | constraint |
---|---|---|
id | integer | uniq |
username | string | uniq |
encrypted_password | string | |
facebook_id or avatar | string | uniq |
Ideally it would be nice to be able to assoicate a user to a Facebook user (or some OpenID user) which allows Mov.io.ly to pull a friend list. Barring that being possible, associating a user with an Avatar would be sufficient |
Important Validations
- Validates unique username
- Validates presence username
- Validates presence password
Relationships
- has_many :favourites
- has_many :users, through: :favourites
- has_many :events
Important Fields
field name | type | constraint |
---|---|---|
id | integer | uniq |
imdb_id | string | uniq |
Assume IMDB ID is unique and never changes |
Important Validations
- Validates unique IMDb ID
Relationships
- belongs_to :user
- belongs_to :movie
Important Fields
field name | type | constraint | default |
---|---|---|---|
id | integer | uniq | |
user_id | integer | ||
movie_id | integer | ||
is_deleted | boolean | false | |
event_timestamp | timestamp | false |
Important Validations
- validates :movie_id, uniqueness: { scope: [:user_id] }
Notes
- Updates to this table only apply if the event driving the update has a timestamp greater than the current
event_timestamp
date
Relationships
- belongs_to :user
- belongs_to :movie
- has_many :user_device_events Solution 2
Important Fields
field name | type | constraint |
---|---|---|
id | integer | uniq |
action | string | |
user_id | integer | |
movie_id | integer | |
device_timestamp | timestamp |
Important Validations
- Validates presence action
- Validates action in ['ADD', 'DEL']
- Validates presence movie
- Validates presence user
- Validates presence device_timestamp
Relationships
- belongs_to :user
Important Fields
field name | type | constraint |
---|---|---|
id | integer | uniq |
user_id | integer | |
name | string |
Important Validations
- validates :name, uniqueness: { scope: [:user_id] }
Relationships
- belongs_to :user
- belongs_to :device
- belongs_to :event
Important Fields
field name | type | constraint |
---|---|---|
user_id | integer | |
device_id | integer | |
event_id | integer |
Important Validations
- validates :event_id, uniqueness: { scope: [:user_id, :device_id] }
GET /users/:user_id/favorite-movies/
Sample response body
{
movies: [
{
id: 123,
imdb_id: 'aaaa'
},
{
id: 234,
imdb_id: 'bbbb'
},
{
id: 345,
imdb_id: 'cccc'
}
]
}
GET /movies/:movie_id/
Sample response body
Example 1:
{
movie: {
id: 123,
name: 'Some movie name',
avatar: 'https://something.s3.amazonaws.com/asdfsadfsadfsafasf/123/',
rotten_tomatoes_url: 'http...',
favourite: null
}
}
Example 2:
{
movie: {
id: 123,
name: 'Some movie name',
avatar: 'https://something.s3.amazonaws.com/asdfsadfsadfsafasf/123/',
rotten_tomatoes_url: 'http...',
favourite: {
id: 3456,
is_deleted: false,
timestamp: 1472608061
}
}
}
GET /movies/:movie_id/favorite-users/
Sample response body
{
users: [
{
id: 123,
username: 'aaaa',
avatar: 'https://something.s3.amazonaws.com/asdfsadfsadfsafasf/123/'
},
{
id: 234,
username: 'bbbb',
avatar: 'https://something.s3.amazonaws.com/asdfsadfsadfsafasf/234/'
},
{
id: 345,
username: 'cccc',
avatar: 'https://something.s3.amazonaws.com/asdfsadfsadfsafasf/234/'
}
]
}
- Decisions should always defer to latest timestamp for conflict resolution
- Timestamps are mobile device's or webserver's timestamp Caveat, this needs to be a correct timestamp
- POSTs should be paginated
POST /users/:user_id/sync-events/
Sample request data
{
events: [
{
action: 'ADD',
movie_id: 123,
timestamp: 1472608060
},
{
action: 'ADD',
movie_id: 345,
timestamp: 1472608061
},
{
action: 'DEL',
movie_id: 123,
timestamp: 1472608072
}
]
}
Sample response body
{}
Status 201 for successful addition
GET /users/:user_id/device/:device_id/sync-events/
Sample response body
{
events: [
{
action: 'ADD',
movie_id: 123,
timestamp: 1472608060
},
{
action: 'ADD',
movie_id: 345,
timestamp: 1472608061
},
{
action: 'DEL',
movie_id: 123,
timestamp: 1472608072
}
]
}