Skip to content

Assignment 3

Application pitch:

Have you ever wondered why it’s so hard to see what all your friends are reading on Goodreads? Tired of having to text your friends a link every time you see a book they might enjoy? BookClub is a social media app that connects readers and book enthusiasts. By facilitating reading as a social activity, BookClub goes beyond tracking individual reading progress and encourages users to read books together.

BookClub enables you to:

  • Easily view and keep track of the books you’ve read, are currently reading, and want to read in the future using folders
  • See what your friends are reading and want to read in a visually engaging format that resembles browsing at a bookstore
  • Rate books and see your friends’ opinions
  • Recommend books directly to specific friends
  • See all the recommendations your friends have given you
  • Invite friends to read books with you

In addition to viewing information and ratings for books, BookClub makes it easy for you to discover what your friends are reading (or want to read), view their opinions on books, and see what they think you might enjoy reading next. BookClub enables users to send their friends personalized reading recommendations by adding books to their Bookbags—places where each person can view the books that their friends have specifically suggested to them—and send invitations to their friends to read books with them. With BookClub, users now have a one-stop shop for discovering and enjoying books with their friends.

Functional design:

User (same as in tutorial)

Purpose: authenticate users

Principle: after a user registers with a username and password, they can authenticate as that user by providing a matching username and password

State:

registered: set User
username, password: registered -> set String

Actions:

register (username: String, password: String, out u: User)

  • create user u with username, password
  • add u to registered

authenticate (username: String, password: String, out u: User)

  • if user u with username, password is in registered, output u

Profile [User]

Purpose: enable users to view an overview of information about other users

Principle: collection of information about a particular user

State:

profiles: User -> one Profile
name: Profile -> one String
info: Profile -> one Data

Actions:

updateName (u: User, n: String)

  • update user’s display name with string n

updateInfo (u: User, i: Data)

  • update user’s info with data i

Session [User] (same as in tutorial)

Purpose: authenticate user for extended period

Principle: after a session starts (and before it ends), the getUser action returns the user identified at the start

State:

active: set Session
user: active -> one User

Actions:

start (u: User, out s: Session)

  • start the session for user u

getUser (s: Session, out u: User)

  • get the user u for the current session

end (s: Session)

  • end the current session

Friend [User]

Purpose: enable a user to define a group of other users that they want to be able to interact more closely with

Principle: after a user becomes friends with another user, they can view specific types of content and perform specific actions with them

State:

friends: User -> set Friend

Actions:

add: (u1: User, u2: User)

  • add u2 to friends for u1

getFriends: (u: User, out userFriends: set User)

  • get all friends for user u

Book [User]

Purpose: contain information and user-generated content related to a particular book

Principle: stores information (description, related items created by users) that is shown when a user selects a book title

State:

info: Book -> one Data
userInfo: Book -> one Data, User
userBooks: User -> set Book, User

Actions:

addInfo (b: Book, i: Data, u: User)

  • add info i to book b’s userInfo; assign author of info as user u

share (b: Book, t: User, f: User)

  • add b, f to userBooks for user t

Folder [Item, User]

Purpose: group items into disjoint categories

Principle: a user can assign items to a folder (maximum of one folder each); then, viewing the contents of that folder will include all the items added

State:

folder: Item -> one String
userFolders: User -> set folder

Actions:

add (i: Item, f: String, u: User)

  • add i to userFolders f for user u

remove (i: Item, f: String, u: User)

  • remove i from userFolders f for user u

getItems (f: String, u: User, out items: set Item)

  • get all items in userFolders f for user u

Rating [Item, User]

Purpose: enable users to view others’ opinions about an item

Principle: enjoyment scores (out of 5) that users have assigned to an item

State:

ratings: Item -> set Number
userRatings: User -> set ratings

Actions:

addRating (i: Item, n: Number, u: User)

  • add n to ratings for i
  • add ratings for i to userRatings for user u

deleteRating (i: Item, n: Number, u: User)

  • remove n from ratings for i
  • remove ratings for i from userRatings for user u

Recommendation [Item, User]

Purpose: enable users to recommend items to each other and view all the items that others have recommended to them

Principle: after a user sends a recommendation to another user, the item recommended will appear in the recipient’s collection of recommendations

State:

recommendations: User -> set Items, User

Actions:

recommend (i: Item, t: User, f: User)

  • add i, f to t.recommendations

getItems (u: User)

  • get all items in u.recommendations

Invitation [Item, User]

Purpose: help users do activities in tandem with other people

Principle: a user can post an invitation to other users regarding a particular item, and others can accept or decline the invitation

State:

invitationsPosted: User -> set Item
invitationsReceived: User -> set Item, User

Actions:

postInvitation (i: Item)

  • add i to invitationsPosted

acceptInvitation (i: Item, u: User)

  • add i, u to invitationsReceived

declineInvitation (i: Item, u: User)

  • remove i, u from invitationsReceived

Synchronizations

app BookClub:

include User
include Book [User.User]
include Folder [Book.Book, User.User]
include Rating [Book.Book, User.User]
include Profile [User.User]
include Session [User.User]
include Friend [User.User]
include Recommendation [Book.Book, User.User]
include Invitation [Book.Book, User.User]

sync login

  • when User.authenticate occurs, trigger Session.start

sync addUserFriend

  • when Friend.add occurs, trigger Profile.updateInfo

sync categorize

  • when Folder.add occurs, trigger Book.addInfo and Profile.updateInfo

sync bookRating

  • when Rating.addRating occurs, trigger Book.addInfo and Profile.updateInfo

sync bookRecommendation

  • when Recommendation.recommend occurs, trigger Book.share and Profile.updateInfo

sync startInvitation

  • when Invitation.acceptInvitation occurs, trigger Folder.add(“currently reading”)

Concept dependence diagram:

Concept dependence diagram

Wireframes:

UI Designs: click here

UI Prototype (includes flows): click here

Notable pages:

Homepage (includes bookshelf-browsing UI and friends' invitations): Homepage

Book page (includes invitation, rating, recommendation, and categorization options): Book page

Book page

Bookbag page (can view all recommendations from friends): Bookbag

Design tradeoffs:

Book categorization: folders versus lists

  • When deciding how users should be able to categorize books as read / currently reading / to read, I had to choose between implementing categories as folders (disjoint, so each book could be in a maximum of 1 folder each) or lists (where each book could be added to multiple lists).
  • I chose to go with folders in order to minimize confusion between the categories (and also because, for simplicity, I wanted to stick to these three categories rather than enabling users to create their own categories). This limited customization and flexibility, but simplified the data relationships and UI.

Specialization: distinguishing ratings from reviews

  • I had the choice to either use book “reviews” as a whole concept (encompassing elements like a numerical rating, written review, upvotes) or differentiate parts like numerical/star ratings as a specialized concept.
  • I chose to specialize and treat numerical ratings as a separate concept because I felt that they served a different purpose than written reviews (overviews of thoughts versus in-depth analyses), and also because it would add more clarity in implementation.

Friend requests versus adding friends

  • Regarding friending, I had to choose between enabling users to send and accept friend requests or just add friends instantaneously.
  • I chose to allow users to just add friends instantaneously, combining the process of requesting and becoming friends (tightening the two actions). This limited the ability for users to screen who their friends will be, but simplified the necessary app processes.