Building a Real-Time Voting System With AWS AppSync and DynamoDb

Lucas Vera ToroLucas Vera Toro
6 min read

This article presents a proof of concept for a real-time voting app facilitating SCRUM grooming sessions using AWS AppSync for a serverless GraphQL API and DynamoDB for storage. It outlines the architecture, data modeling, and implementation details, leveraging AppSync's real-time updates and DynamoDB's single-table design and TTL feature for an efficient voting system. [Summary Generated with AI]

Introduction

In the SCRUM agile methodology, the grooming sessions are meetings where team members get together and agree on how much effort is a particular story worth. This is measured in points. Typically, each team member would give a secret vote for the effort on a particular story, then all votes get revealed and, based on the votes, the team agrees on an effort for a story

In this blog post we will cover a proof of concept for a real-time app that facilitates the voting session that takes place as part of a grooming session. For this, we will use AWS technologies - specifically AWS AppSync for the backend real-time API and DynamoDb for any storage needs

Use Case and Architecture

For the use case, we identify two main actors for the system: “Session Creator” (SC) and “Session Participant” (SP). The SC is responsible for creating a voting session, then sharing the session with other people. Then, the SPs are responsible for joining the session. After enough SPs have joined the session, the SC can start the voting session. Each SP can cast a secret and private vote. After everyone has voted, the SC can reveal the votes and that will conclude the voting session.

For each of the actions described, it’s important to notify all participants and the creator so that updates are visible to everyone.

With that use case in mind, we can lay out a general high-level overview of the system as an AWS AppSync API that allows each actor to perform operations to interact with the system, and at the same time allow for subscription to real-time events for operations made by other actors:

Diagram illustrating a voting session process. A Session Creator initiates and manages sessions, while a Session Participant joins and votes. Both roles receive updates. The AppSync API connects to DynamoDB for data management.

The good thing is that AWS AppSync allows for easily building a serverless graphql API, which supports “subscriptions”. These subscriptions are websockets implementation which allows for server-to-client communication. So a session participant can receive events when other participants joins, vote or when a voting session starts or ends.

Sample Implementation

For implementing this backend we can leverage the usage of javascript and AWS AppSync resolvers to perform all interactions needed. For this API, it’s needed to interact with the dynamodb table and send message to other clients. With AppSync is easy to build an API using javascript resolvers. Interacting with the dynamodb table can be done through direct integrations between AppSync and DynamoDb (no need for lambda or other computing resource).

For sending real-time updates in the graphql subscription, another awesome thing about AppSync is that the heavy lifting is done by AppSync. Using configuration in the schema, we are able to connect a graphql mutation to a subscription so that everytime that mutation is requested, the response will trigger a subscription response to all susbcribed clients (SC and SPs)

Diagram showing two participants interacting with an AppSync API. Participant A joins a session using a GraphQL mutation, and Participant B receives a notification upon joining using a GraphQL subscription.

Schema

For the schema we need to take into account all operations for both the SC and the SPs. Along with operations, we need to define the subscriptions so that clients can receive the real-time updates. As this is a sample implementation, for auth a simple API_KEY approach will be used however in production is recommended to set-up a more robust auth method. The following shows a sample graphql schema with the operations and the subscriptions:

schema {
  query: Query
  mutation: Mutation
  subscription: Subscription
}

type Query @aws_api_key {
  getSession(sessionId: String!): Session
}
type Mutation @aws_api_key {
  createSession(participant: ParticipantInput!): Session
  startSession(sessionId: String!): Session
  endSession(sessionId: String!): Session
  addParticipant(sessionId: String!, participant: ParticipantInput!): Participant
  removeParticipant(sessionId: String!, participantEmail: String!): Participant
  updateParticipantVote(sessionId: String!, participantEmail: String!, vote: Int!): Participant
}
type Subscription @aws_api_key {
  onParticipantAdded(sessionId: String!): Participant
    @aws_subscribe(mutations: ["addParticipant"])
  onParticipantRemoved(sessionId: String!): Participant
    @aws_subscribe(mutations: ["removeParticipant"])
  onParticipantVoted(sessionId: String!): Participant
    @aws_subscribe(mutations: ["updateParticipantVote"])
  onSessionStarted(sessionId: String!): Session
    @aws_subscribe(mutations: ["startSession"])
  onSessionEnded(sessionId: String!): Session
    @aws_subscribe(mutations: ["endSession"])
}

Notice the use of “@aws_subscribe” when defining the subscription. We specify the mutation that will be connected and that’s it! AWS AppSync takes care of the rest.

Data Modeling

For the data modeling needs, we need to keep track of sessions created, session participants that joined a particular session and votes for a session. Using DynamoDb Single-Table Design principles we can keep track of all this information in one table. Using a unique id for each session, we can tie together sessions, participants and votes, as shown in the following image

A table displaying session details with columns for primary key, sort key, and various attributes such as session ID, creator, status, and participant information. The table includes email addresses and timestamps for creation and expiration.

Without going too much into details of single table design, notice that we can store different entities depending on the PK+SK combination. In this case, we identify two entities: Session Details and Session Participants.

  • Session Details

    • PK: SESSION#<session_id>

    • SK: SESSION#<session_id>#DETAILS

  • Session Participant

    • PK: SESSION#<session_id>

    • SK: SESSION#<session_id>#PARTICIPANT#<participant_email>

This gives us flexibility when reading information for a particular session, as we can either read all entries for a session, or read some of it depending on the query pattern we specify.

Another thing to highlight from the data modeling is the usage of DynamoDb TTL. This feature allows for self-cleaning of the dynamodb table of old sessions that may no longer be active. The awesome thing is that it’s done automatically by DynamoDb once the “expirationTime” timestamp has passed.

Code

The following repository contains a partial implementation of this API: GitHub Repository

Conclusion

Using AWS AppSync and DynamoDb, this proof of concept shows an simple implementation for a real-time, serverless voting system that’s useful for SCRUM grooming sessions.

The capabilities of AWS AppSync simplify the implementation of graphql subscriptions for the real-time updates. Using DynamoDb for data storage needs allows for scalable and performant operations while leveraging the use of single-table design for cohesive data storage and DynamoDb TTL for self-cleaning of inactive sessions.

This architecture allows for scalable and low-latency interactions between participants in a grooming session, resulting in effective and efficient SCRUM ceremonies.

References

2
Subscribe to my newsletter

Read articles from Lucas Vera Toro directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Lucas Vera Toro
Lucas Vera Toro

Technical Architect and Developer with 10 years of experience, specializing in scalable and efficient digital products powered by cloud technologies.