Skip to content

Assignment 3: Convergent Design

Pitch

The name of the app will be Reportter. It will be a news article posting/sharing platform that has the interactivity features of Twitter, mainly layered commenting, following, reposting, etc... Its main improvement over Twitter will be to make clear distinctions between regular users and reporters, and opinion posts and articles. This seemingly simple addition will make the app much more user-friendly, safe from misinformation, profitable for reporters, and more. The main way we will be able to achieve this is to add some extra functionality to reporters (like posting articles with custom styles and formatting, or hiding content behind a pay barrier) at the cost of requiring some validation.

It is apparent from our user interviews that there are currently a lot of people using Twitter to get access to the news, mainly because of the unique interactive environment it creates. However, they all have problems (concerns about misinformation, usability, etc.) that are mainly direct results of the app not being centered around news reporting. Hopefully, our app will be able to fix those issues and offer those users a better alternative.

Functional design

Concepts

Article

Purpose: Display formatted news articles
OP: News articles written in some markup language are displayed on the website
State:

articleID: set Int
articleContent: articleID -> one String
articleID: set Int
articleContent: articleID -> one String

Actions:

addArticle(s: string, out x: articleId)
    adds a random Int id to articleID
    Sets id.articleContent = s
    returns id

getArticle(id: articleID, out s String)
    returns id.articleContent
addArticle(s: string, out x: articleId)
    adds a random Int id to articleID
    Sets id.articleContent = s
    returns id

getArticle(id: articleID, out s String)
    returns id.articleContent

Comment[Target]

Purpose: Display short text directly related to some other content
OP: Site users post short texts related to other content as comments. Comments are displayed together with the main content.
State:

commentID: set Int
commentContent: commentID -> one String
commentParent: commentID -> one Target
commentID: set Int
commentContent: commentID -> one String
commentParent: commentID -> one Target

Actions:

addComment(s: String, id: Target, out x : commentID)
    adds random Int x to commentID
    x.commentContent = s
    x.commentParent = id
    return x
addComment(s: String, id: Target, out x : commentID)
    adds random Int x to commentID
    x.commentContent = s
    x.commentParent = id
    return x

User

Purpose: Allow other concepts to distinguish between people and store data related to people across different uses
OP: To use most features of the app, everyone must create and login in as a user, which allows other concepts to uniquely identify who is currently using the app
State:

userID: set Int
username: userID -> one String
password: userID -> one String
userID: set Int
username: userID -> one String
password: userID -> one String

Actions:

createUser(username: String, password: String, out x : userID)
    adds random Int x to userID
    x.username = username
    x.password = password
    return x

login(username: String, password: String)
    let x be the userID s.t. x.username = username
    if x.password == password, create a session for user x, meaning all actions in this session will have access to x
createUser(username: String, password: String, out x : userID)
    adds random Int x to userID
    x.username = username
    x.password = password
    return x

login(username: String, password: String)
    let x be the userID s.t. x.username = username
    if x.password == password, create a session for user x, meaning all actions in this session will have access to x

ContentAuthoring[Creator, Content]

Purpose: Keeping track of what content was created by whom OP: When creating any new type of content, some kind of user info must be provided, which is stored to be used by other concepts State:

contentOwned: Creator -> set Content
contentOwned: Creator -> set Content

Actions:

addContent(creator: Creator, content: Content)
    creator.contentOwned += content

getCreator(content: Content, out creator: Creator)
    return creator such that creator.contentOwned contains content
addContent(creator: Creator, content: Content)
    creator.contentOwned += content

getCreator(content: Content, out creator: Creator)
    return creator such that creator.contentOwned contains content

ReporterValidation[Target]

Purpose: Add extra identification to users authorized to post certain content to create accountability and credibility
OP: To post certain content (news articles), users must apply for validation which is then approved by the server, which might limit some of their privacy options and require they post some extra information about themselves
State:

validationID: set Int
validatedUser: validationID -> one Target
validationDate: validationID -> one Date
validationRequests: set Target
validationID: set Int
validatedUser: validationID -> one Target
validationDate: validationID -> one Date
validationRequests: set Target

Actions:

applyForValidation(user: Target):
    validationRequests += user

confirmValidation(user: Target):
    user in validationRequests
    validationRequests -= user
    adds random Int x to validationID
    x.validatedUser = user
    x.validationDate = current date

checkIfValidated(user: Target, out result : boolean):
    return true of there exists an id x such that x.validatedUser = user
    return false otherwise
applyForValidation(user: Target):
    validationRequests += user

confirmValidation(user: Target):
    user in validationRequests
    validationRequests -= user
    adds random Int x to validationID
    x.validatedUser = user
    x.validationDate = current date

checkIfValidated(user: Target, out result : boolean):
    return true of there exists an id x such that x.validatedUser = user
    return false otherwise

Bio[Target]

Purpose: Provide background info for users
OP: Users write texts about their background relating to the context they use the site for, which is displayed to all. Might be optional or compulsory
State:

bioId: set Int
bioTarget: bioID -> one Target
bioContent: bioID -> one String
bioId: set Int
bioTarget: bioID -> one Target
bioContent: bioID -> one String

Actions:

addBio(user: Target, s: String)
    adds random Int x to validationID
    x.bioTarget = user
    x.bioContent = s

hasBio(user: target, our result: boolean)
    returns true if there exist id s.t. id.bioTarget == user
    else false
addBio(user: Target, s: String)
    adds random Int x to validationID
    x.bioTarget = user
    x.bioContent = s

hasBio(user: target, our result: boolean)
    returns true if there exist id s.t. id.bioTarget == user
    else false

Subscription[User, Content]

Purpose: Allow some content to only be possible through monthly-paid subscription OP: Content creators mark some of their content as paid. Only users who pay for the subscription can see or access this content.
State:

subscribers: User -> set User
paidContent: User -> set Content
subscribers: User -> set User
paidContent: User -> set Content

Actions:

subscribeTo(from: User, to: User)
    to.subscribers += from

makePaid(creator: User, content: Content)
    creator.paidContent += content
subscribeTo(from: User, to: User)
    to.subscribers += from

makePaid(creator: User, content: Content)
    creator.paidContent += content

Instantiations

Article = Article
CommentToArticle = Comment[articleId]  
CommentToComment = Comment[commentId]
User = User
ArticleAuthorship = ContentAuthoring[userId, articleId]
CommentAuthorships = ContentAuthoring[userId, commentId]
ReporterValidation = ReporterValidation[userId]
Bio = Bio[UserId]
Subscription = Subscription[userId, articleId]
Article = Article
CommentToArticle = Comment[articleId]  
CommentToComment = Comment[commentId]
User = User
ArticleAuthorship = ContentAuthoring[userId, articleId]
CommentAuthorships = ContentAuthoring[userId, commentId]
ReporterValidation = ReporterValidation[userId]
Bio = Bio[UserId]
Subscription = Subscription[userId, articleId]

Synchronizations

Let curUser be the userId of the user currently in session for the sake of simplicity. This means any actions that directly/indirectly requrie curUser need some sort of authorization/logging in, so it is also a syncronization to the User concept.

Between Article and ArticleAuthorship

Whenever we add an article, we also have to define its creator

articleId = Article.addArticle(s) 
ArticleAuthorship.addContent(curUser, articleId)
articleId = Article.addArticle(s) 
ArticleAuthorship.addContent(curUser, articleId)

Between Comment and CommentAuthorships

Whenever we add a comment, we also have to define its creator

commentId = CommentToComment.addComment(s, id) 
userComments.addContent(curUser, commentId)
commentId = CommentToComment.addComment(s, id) 
userComments.addContent(curUser, commentId)
commentId = CommentToArticle.addComment(s, id) 
userComments.addContent(curUser, commentId)
commentId = CommentToArticle.addComment(s, id) 
userComments.addContent(curUser, commentId)

Between Article and ReporterValidation

Only validated users can post articles

if ReporterValidation.checkIfValidated(curUser) == false:
    throw Error
Article.addArticle(s)
if ReporterValidation.checkIfValidated(curUser) == false:
    throw Error
Article.addArticle(s)

Between ReporterValidation and Bio

Validated users need to have a bio

if Bio.hasBio(user) == false:
    throw Error
ReporterValidation.confirmValidation(user)
if Bio.hasBio(user) == false:
    throw Error
ReporterValidation.confirmValidation(user)

Between Article, Subscription, ArticleAuthorship

To get an article, you must have subscriber access to it if it is paid

articleAuthor = ArticleAuthorship.getCreator(article)

if article not in Subscription.articleAuthor.paidContent
    or articleAuthor == curUser
    or curUser in Subscription.articleAuthor.subscribers:
    return Article.getArticle(articleId)
else:
    throw Error
articleAuthor = ArticleAuthorship.getCreator(article)

if article not in Subscription.articleAuthor.paidContent
    or articleAuthor == curUser
    or curUser in Subscription.articleAuthor.subscribers:
    return Article.getArticle(articleId)
else:
    throw Error

Between Subscription, ArticleAuthorship

Only the article author can make an article paid

articleAuthor = ArticleAuthorship.getCreator(article)

if curUser != articleAuthor:
    throw Error
else:
    Subscription.makePaid(curUser, article)
articleAuthor = ArticleAuthorship.getCreator(article)

if curUser != articleAuthor:
    throw Error
else:
    Subscription.makePaid(curUser, article)

Synergies

By seperating out the ContentAuthorship content from User, Article, and Comment, we open the way to many different ideas that could be implemented in the future vey easily with just some simple adjustments to the syncs: anonymous comments, articles pubished by not users but news agencies, articles with multiple authors, etc... The opportunities are endless.

The bio concept is usually used to give the user the opportunity to add some facts/backgorund about themselves. Here, we have reappropriated it to be a part of the validation process.

Dependency Diagram

Wireframes

https://www.figma.com/proto/I6eoA8pZP6OoThpoevWNbi/WireFrame-Reportter?type=design&node-id=2-2&t=OXU4erB8UBtCGlkF-1&scaling=min-zoom&page-id=0%3A1&starting-point-node-id=2%3A2&mode=design

Design Tradeoffs

A Concept for Inter-Concept Relations?

For the functional design part of the assignment, the concept that kept me wondering the most was the ConceptAuthoring. Just looking at it by itself, it seems weird to have it as a separate concept, but I feel like it is a much better decision than merging it with another concept. With this separate design, we can ensure that we could, if we wanted to, have users that cannot post anything, posts that have no user associated with them, or posts that are posted by entities other than users, and much more. Merging the ConceptAuthoring concept into for example User would maybe make it clearer and more concise, but it would also mean we would have to commit to a lot of design choices, with little opportunity for change, extension, or reusability.

No Deletions or Mutations

It was an intended design choice to not include any deletion or mutation actions like deleteArticle(articleID) or changeArticleContent(articleID) in all of the concepts. This might seem counter-intuitive, as being able to update your posts to get rid of typos or delete content that has since become outdated is great for convenience, but I think directly conflicts with the goal of the app to have clean and safe information on the internet. Especially, the ability to mutate posts creates many opportunities for malicious users. What if a user creates posts an article with correct information, gets lots of interaction and starts being recommended, and alters the content of the article to mislead and disinform others? The fact that the things posted on the website are unalterable adds an extra layer of trust.

Hasles For Reporters

Reporters have a lot of extra trouble they have to go through in this app, but I think it is very much worth it. They will be only a small percentage of users posting most of the content, and making sure they are reliable sources is of utmost importance.