
Serverless Layers with Monorepo
Introduction
In the world of cloud development, combiningServerless Framework with aMonorepo offers a smart way to handle and deploy multiple applications. This article breaks down the process of creating aCloudFormation stack using Serverless Framework. Each application, neatly packed within a Monorepo, gets its ownLambda function. Join us as we explore the easy integration of serverless tech and the practical advantages of using a Monorepo, uncovering the steps to set up a flexible and organized infrastructure.
Starting with Monolith
In the early stages of a project, it's often a good idea to begin with a monolith—a single, straightforward architecture. This simplicity is beneficial when the project is just getting started because we might not have a clear picture of the system's boundaries, and predicting how it will grow can be tricky.
By starting with a monolith, developers can handle the uncertainties of the initial phase more easily. As the project advances and we add more features, there might come a time when it makes sense to break off certain functions into smaller pieces called microservices. This step allows for a more organized and scalable system that can adapt as the project evolves.
Keyword: Modularity
Building on the foundation of a monolith, the key to success lies in a modular code organization, where "bounded contexts" are well-contained and independent.
In an ideal world, picture each module neatly residing in its own folder within our monolith. Transforming a module into a new application should be a seamless transition; if it requires excessive effort, it's a clear sign that something needs adjustment.
Embracing modularity ensures flexibility as the project evolves, promoting a codebase that is both adaptable and comprehensible.
Monorepo Overview
This monorepo is organized around the 'apps' folder, containing two Fastify applications:
- Customers
- Orders
Each application within the 'apps' folder follows a consistent structure:
- src
- handlers
app.ts
(Lambda entry point)
- routes
routeA.ts
(Route definitions for specific functionalities)routeB.ts
(Additional route definitions)
- handlers
app.ts
(Application entry point)server.ts
(Server setup and configuration)
apps/customers/app.ts
import'module-alias/register'importfastify,{FastifyInstance,FastifyServerOptions}from'fastify'importautoloadfrom'@fastify/autoload'import{join}from'path'exportdefaultfunctioncreateApp(opts?:FastifyServerOptions,):FastifyInstance{constdefaultOptions={logger:true,}constapp=fastify({...defaultOptions,...opts})app.register(autoload,{dir:join(__dirname,'routes'),options:{prefix:'/customers'},})returnapp}
apps/customers/routes/getCustomers.ts
import{FastifyInstance}from'fastify'exportdefaultasyncfunction(app:FastifyInstance):Promise<void>{app.get('/',async()=>{return[{id:1,name:"Johnny Walker"},{id:2,name:"Jack White"}]},)}
apps/customers/handlers/app.ts
importawsLambdaFastifyfrom'@fastify/aws-lambda'importcreateAppfrom'../app'constapp=createApp()constproxy=awsLambdaFastify(app)exportconsthandler=proxy
apps/customers/server.ts
importcreateAppfrom'./app'constapp=createApp()app.ready().then(()=>{app.listen({port:4001},(err)=>{if(err!=null){app.log.error(err)process.exit(1)}})})
(Order app has the same structure).
Going Serverless
In our move to deploy the entire project on the cloud using Serverless Framework, we aim to avoid bundling all dependencies (as listed in package.json) for each Fastify application's lambda function. To achieve this, we'll create a sharedlayer that can be utilized by all applications in the monorepo. This layer approach streamlines deployment, ensuring efficient use of resources across multiple functions while simplifying maintenance.
Using the "serverless-layers" Plugin
Introducing the "serverless-layers" plugin (https://www.serverless.com/plugins/serverless-layers): a user-friendly tool that automates the creation of layers. By default, it utilizes the package.json found in the project's root, simplifying the layer setup process. This plugin streamlines the integration of shared dependencies across multiple functions, enhancing efficiency and reducing redundancy in your Serverless deployment.
serverless.yml
service:fastify-serverless-layerframeworkVersion:'3'provider:name:awsruntime:nodejs18.xregion:eu-south-1deploymentBucket:name:fastify-serverless-layer-bucketserverSideEncryption:AES256plugins:-serverless-plugin-typescript-serverless-tscpaths-serverless-layers-serverless-deployment-bucket-serverless-offlinecustom:serverless-layers:functions:# optional-customers-ordersdependenciesPath:./package.jsonfunctions:customers:handler:apps/customers/src/handlers/app.handlerevents:-http:path:/customers/method:ANY-http:path:/customers/{any+}method:ANYorders:handler:apps/orders/src/handlers/app.handlerevents:-http:path:/orders/method:ANY-http:path:/orders/{any+}method:ANY
In the provided serverless.yml, each Lambda function (e.g., 'customers' and 'orders') is configured with an entry point pointing to an application within the monorepo. For instance, the 'customers' function is linked to apps/customers/src/handlers/app.handler. These functions are subsequently associated with specific API Gateway routes, like /customers/ and /orders/. This setup streamlines the integration of Fastify applications with AWS Lambda, simplifying the routing configuration for the Serverless deployment.
Conclusions
The project code is in this GitHub repository:fastify-serverless-layer.
This journey guided us from building a CloudFormation stack based on a modular monolith to evolving into microservices, demonstrating the project's flexible design in practice.
Top comments(0)
For further actions, you may consider blocking this person and/orreporting abuse