⚠️ ⛔ WARNING ⛔⚠️ This project is NOT production ready and can change at any time. You WILL lose your data :)
Plutomi is an applicant tracking system that streamlines your entire application process with automated workflows at any scale.
Having worked at a company that needed to recruit thousands of contractors every month, implementing custom processes in our acquisition flow was challening. This would be things like address validation, cloning openings and stages, duplicate applicant detection, automatically engaging leads based on our forecasts, programmable applicant routing logic, automatically closing texts that didn't require a response, intraday queues for applicant actions, simpler PII scrubbing for third party integrations, more customizability of opening and stage styling, and many more.
It would have benefited us to have an open platform to contribute to and build upon, as well as one where we didn't have to continuously tip toe around performance limits. This project is our attempt to do just that.
In your recruiting flow, you can create openings
, which people can apply to. An opening can be anything from a job, a location for a delivery company, or a program like a summer camp.
In these openings, you can create stages
which are individual steps for your application. You can add questions which applicants can answer, and setup automatic move rules that determine where applicants go next depending on their answers or after a certain time period.
An opening for a delivery company might look like this:
Opening name: New York City
Stage order:
- Questionnaire - Collect basic information of an applicant. If an applicant does not complete this stage in 30 days, move them to the Waiting List.
- Waiting List - An idle pool of applicants
- Document Upload - Collect an applicant's license
- Final Review - Manually review an applicant's license for compliance
- Ready to Drive - Applicants that have completed your application
- Install Docker
- Create a Hosted Zone in Route53 with your domain
- Create a verified identity with your domain in SES
- Create a certificate for your domain in AWS Certificate Manager
☝️ Will try to add the Route53 / ACM / SES setup to CDK eventually #390
Command | Function |
---|---|
npm run dev | Will start the NextJS frontend on port 3000 and the Express API on port 4000 |
npm run next | Will start NextJS only |
npm run api | Will start the API only |
cdk deploy | Will deploy the specified stack(s) |
cdk destroy | Will destroy the specified stack(s) |
cdk synth | Emits the synthesized CloudFormation template for the stack(s) |
For more information on AWS CDK, please visit the docs page.
The project is 100% TypeScript. Would appreciate any assistance on types as we're definitely not the best 😅
Docker is used to run our Express API on Fargate.
ALL infrastructure is managed by AWS CDK.
The frontend runs on the CDK construct of the Serverless-Nextjs component. The reason being is we wanted everything managed by CDK and this provides an awesome way to do just that. The SLS component brings with it the Next API routes using Lambda but there are a couple of downsides (some of them are listed here) and we won't be using them.
Typical 'monolith' express app on an autoscaling Fargate cluster.
We considered API Gateway + Lambda but we kept running into quirks that essentially wipe out all of the gains from "only focusing on business logic". Here is an example of a fun (4 year old) bug: Unable to change parameter names in API Gateway without tearing the route down and redeploying! Another main complaint is local development, or lack there of. Or cold starts no matter how infrequent they might be. Or performance (we were getting consistently faster response times like in this test by the folks at Trek10). Or cost at high throughput.. mainly API Gateway 😅
To be clear, we will still use lambda for background tasks such as queues, DynamoDB streams, email sending, etc. just not for the main API of the site. Fargate gives us the best of both worlds, and we're very happy with it!
Schema is subject to change but I will try to keep this updated as much as I can
We're using a single table design for this project. If you're new to Dynamo, I recommend watching these talks by Alex DeBrie and Rick Houlihan first:
-
Alex DeBrie @ re:Invent 2020 - Data modeling with Amazon DynamoDB – Part 1
-
Alex DeBrie @ re:Invent 2020 - Data modeling with Amazon DynamoDB – Part 2
-
Rick Houlihan @ re:invent 2018 - Advanced Design Patterns for DynamoDB (DAT401)
-
Rick Houlihan @ re:invent 2019 - Advanced design patterns for DynamoDB (DAT403-R1)
Also, don't forget to buy THE DynamoDB Book by Alex ;)
To play around with the data model locally, you can download NoSQL Workbench and import the NoSQLWorkbench.json file into it. You can even export the table to your AWS account and generate queries in Python, JavaScript, or Java.
I've created a spreadsheet with access patterns and use cases if you prefer that. It helps to follow along with NoSQL Workbench on your own machine or you can view the pictures in the Schema folder.
You might have noticed that some(!) sort keys (SK, GSI1SK, GSI2SK) have the ENTITY_TYPE
prefixed (e.g. APPLICANT_FILE
). This is intentional and it's to retrieve these child items when doing a query on the parent.
For example, if we want to retrieve an applicant, we might also want to retrieve their files, notes, and responses. We can do that with a single query: PK = APPLICANT#{APPLICANT ID} and SK begins_with(APPLICANT)
:)
Some partitions will need to be sharded in the future, especially for high RCU queries at scale (get all applicants in an org, in a stage, in an opening, all webhook history, etc.). I am not going to bother with this for now but it is on my radar!
Another thing to note is that Dynamo has a 400kb limit per item. This means that we do have to set some limits, specifically around entities that can have their order re-arranged (MAX_CHILD_ENTITY_LIMIT
). The limit is on the parent entity, not the re-arrangeable entity itself. Things like stages in an opening or questions & rules in a stage are affected since we have to store their order in their parent item. In practice, even at millions of applicants, it is very unlikely to have hundreds of these entities under one parent.
Argument of type 'this' is not assignable to parameter of type 'Construct'
Make sure all of your @aws-cdk/*
dependencies are running the same version + make sure whatever you are using in the construct is actually being imported at the top of the file
ERROR [internal] load metadata for public.ecr.aws/sam/build-nodejs
Try running this command: aws ecr-public get-login-password --region us-east-1 | docker login --username AWS --password-stdin public.ecr.aws/sam/build-nodejs
To make a contribution, submit a pull request into the main
branch. You will be asked to sign a Contributor License Agreement for your PR. You'll only have to do this once.
This project tries to follow Semantic Pull Requests some what. Your PR title should have the following format:
Type | Description |
---|---|
✨ feat: OR enhancement: | Added a new feature or enhancement |
🐛 fix: | Squashed some bugs! |
📖 docs: | Updated documentation, readme, examples |
🚨 test: | Added / modified tests |
🧹 chore: | Maintenance, cleanup, comment removal, refactoring, etc. If it doesn't fit above, it goes here |
Example: 🐛 fix: Removed the double modals popping up on login
This project is licensed under the GNU AGPLv3
license. It can be viewed here or in the LICENSE.md file.
For any questions, please submit an issue or email contact@plutomi.com!
Thanks goes to these wonderful people (emoji key):
Jose Valerio 💻 🚇 🚧 |
praguru14 💻 🚧 |
Jose Valerio 💻 🚧 🐛 |
This project follows the all-contributors specification. Contributions of any kind welcome!