Aula - Behind the scenes of The Learning Experience Platform - Our stack
Tags: react, startup, serverless, node
Published: 2021-02-08 17:00:49

> Aula is the Learning Experience Platform (LXP) for higher education. We make it easy for educators to create community-first learning experiences that truly engage students. 

{% youtube 6Mi5wySZUM0 %}

## 😅 TL;DR
* Terraform.
* React, React Native and Redux.
* AWS Lambda/serverless running on Node.js, S3, SNS, SQS, etc.
* MongoDB Atlas.

## ⚙️ The stack

Let's dive right in! 🏊‍♂️

### ⌨️ Javascript

Our entire stack is written in Javascript. This includes our web applications, mobile client and all of our back-end services.

Using modern Javascript throughout our codebase feels great. It gives our small team of full-stack developers a consistency, which coupled with Javascript's flexibility empowers us to be productive and move at a very fast pace.

### 🏛 Isolated, reproducible environments

We work with higher education institutions, a regulated sector where data privacy and security are of the utmost importance. No Aula back-end service should be able to access data from multiple institutions. Additionally, institutions should scale according to their needs in a cost effective manner.

That is why each partner institution we work with gets their own fully isolated environment. In practical terms, this translates into a separate sub-account and [Virtual Private Cloud (VPC)](https://aws.amazon.com/vpc/?vpc-blogs.sort-by=item.additionalFields.createdDate&vpc-blogs.sort-order=desc) on AWS. Traffic can go in and out of the VPC through a [NAT instance](https://docs.aws.amazon.com/vpc/latest/userguide/VPC_NAT_Instance.html) running on an [EC2](https://aws.amazon.com/ec2/?ec2-whats-new.sort-by=item.additionalFields.postDateTime&ec2-whats-new.sort-order=desc) machine via [Docker](https://www.docker.com/).

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/jh1xlwlcyoo144sxrxbk.png)

Every time we sign a new partner institution we set up a VPC and all the other AWS resources we require. This is an involved and error prone process and that is why we let machines do it!

We heavily rely on [infrastructure as code](https://en.wikipedia.org/wiki/Infrastructure_as_code) and use [Terraform](https://www.terraform.io/) to define and provision new environments. Terraform removes the human error factor and makes creating new environments a far easier process.

### 🚛 Storage

Each use case requires a particular type of storage. As previously mentioned, each store is separate per institution and lives within their corresponding VPC.

* [MongoDB](https://www.mongodb.com/2) via [Atlas](https://www.mongodb.com/cloud/atlas) for our long-lived application data: posts, comments, messages, assignments, etc.
* [ElasticSearch](https://www.elastic.co/elasticsearch/) running on EC2 to power our search feature.
* [S3](https://aws.amazon.com/s3/) to store images, videos and other files users can share on the platform.
* [Redis](https://redis.io/) for our [WebSocket](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API) server instances to communicate and deliver real-time updates to our front-ends.

### 🏎️ Microservices

At Aula, we have built our back-end around the [microservices](https://microservices.io/) paradigm. We believe it to be the best fit for our use case because:

* We can collaborate more easily without conflicts.
* Services can auto-scale independently in a much more cost-effective way.
* Issues affecting one service can be isolated, allowing the rest of the application to function as usual.

We minimise the drawbacks of microservices with:
* Shared utility libraries to reduce boilerplate.
* Tooling to manage deployment complexity.

Most of our back-end logic runs on [AWS Lambda](https://aws.amazon.com/lambda/) and the [Serverless framework](https://www.serverless.com/) with the Node.js runtime. This allows us to focus on the business logic rather than managing servers. We find that Lambda functions scale phenomenally well. Migrating from a Docker/EC2 model to Lambda has truly transformed Aula Engineering, dramatically improving stability and developer experience.

Let's go through the journey of two common workflows at Aula, such as *creating a post* and *sending a message*, and see what happens under the hood.

![Overall back-end architecture](https://dev-to-uploads.s3.amazonaws.com/i/8xafifdd76yfzkvvxz04.png)

* The client makes an HTTP request to an API endpoint.
* [API Gateway](https://aws.amazon.com/api-gateway/), managed by AWS invokes the corresponding Lambda function that implements the route handler.
* The Lambda function will handle authentication, run some business logic, save content to the database as well as pushing an event to our [SNS](https://aws.amazon.com/sns/) event bus. This will allow other services to react to the event in a non-blocking, fault-tolerant way.
* An [SQS](https://aws.amazon.com/sqs/) queue for live updates picks up the event and triggers a new Lambda function that forwards the event to our Websocket server.
* Our Websocket server runs on [Fargate](https://aws.amazon.com/blogs/aws/aws-fargate/) and communicates across instances via Redis. The new post or message event reaches other relevant users via sockets! Fargate removes a lot of the complexity of scaling a cluster of containers.

The Lambda, SNS, SQS pattern described above is used for many other features like push notifications or search indexing. The use of [dead letter queues (DLQs)](https://en.wikipedia.org/wiki/Dead_letter_queue) when a particular queue message fails to be processed provides us with great error recovery capabilities. Messages are not lost and processing can be retried.

### User analytics

Aula is a data-driven company. We rely heavily on user analytics to make product decisions. Furthermore, educators using Aula depend on student engagement data to identify those who may be falling behind and need a bit of help.

It's essential that our analytics pipeline is reliable.

![Analytics pipeline](https://dev-to-uploads.s3.amazonaws.com/i/cs8ua3n8uvtwc7thfl7d.png)

Both our clients and back-end services may generate analytics events. These events are processed by a lambda function that uses [Kinesis Firehose](https://docs.aws.amazon.com/firehose/latest/dev/what-is-this-service.html) to aggregate events into an S3 bucket. Analytics events and our MongoDB store are synchronised into our [Snowflake](https://www.snowflake.com/data-warehousing-glossary/virtual-warehouse/) data warehouse, and are transformed for consumption by [Metabase](https://www.metabase.com/), our [Business Intelligence](https://en.wikipedia.org/wiki/Business_intelligence) tool.

![Student engagement chart](https://dev-to-uploads.s3.amazonaws.com/i/08nxvkb9isgddcjr8pk5.png)

### ⚛️ Web and mobile clients

At Aula, we love [React](https://reactjs.org/) for its one-way data flow, composability and huge community. We use React in all our client applications. On mobile, [React Native](https://reactnative.dev/) gives us all the benefits of React plus access to native features when needed.

On top of being fantastic tools, their level of consistency keeps the mental burden on the team to a minimum, which allows us to be flexible and work across the product easily.

In order to avoid repetition when building features for both web and mobile, both platforms share the whole Redux store, actions, reducers and selectors. The re-use also results in fewer bugs and a more consistent experience across devices 🐛!

![sharing code between mobile and web](https://dev-to-uploads.s3.amazonaws.com/i/e0fcatacay317qlot1bl.png)


### ♻️ Build, test and deploy workflow

Managing a collection of microservices, multiple front-ends and many different environments can certainly become cumbersome without the appropriate tooling. We have automated all these processes with a combination of off-the-shelf products and our own Aula [CLI](https://en.wikipedia.org/wiki/Command-line_interface).

All of our code lives in a [monorepo](https://en.wikipedia.org/wiki/Monorepo) hosted on Github, which we also use for issue-tracking. When a PR is open, a [Circle CI](https://circleci.com/) job kicks off that lints and tests the changes.

![PR checks](https://dev-to-uploads.s3.amazonaws.com/i/j8u76ss98oxs3oooj882.png)

Once a PR is merged, another Circle CI job is spun, which:
* Builds the front-ends and services that have changed.
* Creates a new monorepo version.
* Deploys the version to our staging environment.
* Runs E2E tests written in [Cypress](https://www.cypress.io/).
* Deploys the version to our internal Aula environment. We [dog-food](https://en.wikipedia.org/wiki/Eating_your_own_dog_food#:~:text=Eating%20your%20own%20dog%20food%20or%20dogfooding%20is%20the%20practice,a%20kind%20of%20testimonial%20advertising.) our own product 🐶!

![Jobs that run after merge on Circle CI](https://dev-to-uploads.s3.amazonaws.com/i/9tgu8xi18ak9fs9yb07f.png)

We trigger deployments to production via Aula CLI, which in turn spins up jobs on Circle CI.

The Aula CLI also provides tooling around other common workflows such as test user creation and individual service deployments.


### 🚨 Logging, monitoring and alerting

Observability is paramount when maintaining a SaaS product. You should not wait until your users tell you something is not quite working before you act 😱!

All our back-end services output logs and metrics into [CloudWatch](https://aws.amazon.com/cloudwatch/). Alarms are then set based on our [SLAs](https://en.wikipedia.org/wiki/Service-level_agreement). For example, when the availability of a REST endpoint goes below 99.9% for 1 minute, an alarm is raised and routed to [Opsgenie](https://www.atlassian.com/software/opsgenie), our on-call management tool.

Alerts are periodically tuned to maximise how useful they are whilst keeping noise to a minimum.

Client-side errors are tracked via [Sentry](https://sentry.io/).

## 🚀 Next

We have a public [product portal](https://portal.aula.education/tabs/8-in-development) where you can peek into our roadmap and see what we're working on right now.

As a team, we continuously improve our processes and technologies. For example, we're introducing Typescript as we build a [brand-new content editor](https://portal.aula.education/c/59-aula-editor-v2-0) based on [Slate](https://www.slatejs.org/examples/richtext).

## 👋 Join us

Do you want to join a remote and diverse team, work with exciting technologies and build a community-first platform that helps educators make learning truly engaging? We're looking for [Senior Software Developers](https://aulaeducation.recruitee.com/o/senior-software-developer
)!