Building a Real-Time Voting System With AWS AppSync and DynamoDb
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:
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)
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
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
SCRUM Backlog Grooming: Scrum Grooming Meeting: Backlog Refinement Meetings Best Practices
AWS AppSync:
What is AppSync: AWS Documentation
Subscriptions for Real-time updates in AppSync: AWS Documentation
DynamoDb:
Main Documentation Page: AWS Documentation
Single-Table Design: The What, Why, and When of Single-Table Design with DynamoDB
No SQL Workbench for Dynamodb Data Modeling: AWS Documentation
Sample code implementation: GitHub Repository
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.