
Creating a Library for Cloud Projects
In my way to evolve and aggregate services to my website, I started to think about integrate a comment section to each post. I read about services like Disqus and other less known like Commento. But the same thoughts that I had when I started to develop my website, came to me again: what if the service goes offline?
Then, I did a fast search in GitHub for a ready-to-use open source project to store and display comments, where I found a very simple project called serverless-comments. It uses the AWS Lambda service to handle the requests, saves the comments at DynamoDB, uses Google reCAPTCHA to prevent spam, and sends an e-mail to the website admin when a there’s a new comment. I could use this under permission, change some points to enhance or to adapt to my needs, but then I started to think again: what if I developed my own comments system?
Then I started to plan my comments system. I’m using a Serverless Framework Typescript template, with a code organization following the Hexagonal Architecture. The packages are organized like the following:
- adapters
- storage
- database
- captcha
- models
- repositories
- services
- functions
For database, I planned to support AWS DynamoDB and Redis (as an open source option), for storage will support AWS S3, and for captcha obviously Google reCAPTCHA. I also developed unit and integration tests to help this development, to ensure the different APIs will work under the same interface but in a common way. This forced me to enhance my skills writing and enhance tests, and using them to make the application code even better.
While I was creating the application structure, I perceive that the common part of the application (the adapters, the base repository and some services) can be moved to its own library, then I create the UFC - Utilities for Cloud to make available not only the library, but also the knowledge used to develop it, like the testing configuration and the process to publish the application to NPM itself.
Implementation - Redis
I confess I had some trouble to create a common interface to support systems so different as DynamoDB and Redis. But I
Redis was created as a key value cache, but through the years it received many features that allow it to work as a NoSQL database. Its website contains many tutorials to use the built-in features to create indexes and store objects, and there are many plugins that can be added like RedisJSON and RedisQuery to expose more advanced features. But usually these plugins are not installed on the most common providers, so I choose to develop the index feature by using Sets and Sorted Sets built-in objects.
The first iteration of the RedisIndex handles only the entity key (the primary key), and differently from the indexes created by the DynamoDB and MongoDB, it doesn’t store the data, only the object keys. It’s instantiated by the RedisAdapter’s constructor, adds the object keys when an object was created and remove the key when the object was deleted. Finally, the query function from RedisAdapter uses the index to find the keys, which are then used to directly get the items from the Redis database.
The next iteration of the RedisIndex will allow using different attributes to construct the items, and mapping then to the object keys to retrieve the items from the database.
Roadmap
I plan to support many more backends and services. About the backends:
- Storage
- AWS S3
- Filebase (an aggregator of Web3 storage providers, uses S3 API)
- Google Firebase Cloud Storage
- Cloudflare R2
- Database
- Google Cloud Firestore
- MongoDB
- E-mail
- AWS Simple Email Service
- Google Firebase Trigger Email extension
- Queue
- AWS SQS
- Redis (using Redis Streams)
- Apache Kafka
- Scheduler
- Firebase Scheduler
And about the planned features to be implemented under the hood:
- Add large size attributes to the entities, and persist them to a storage provider instead of a database;
- Add multi region access to the queues, either adding inter region communication or exposing the endpoint as a AWS Global Accelerator;
- Add a feature bundle, using all adapters from the same provider if available; this can be passed to a IoC library to be registered;