yaantra
v1.0.0
Published
Serverless tooling built from existing AIS tooling: - AIS Service Discovery - Jedlik
Downloads
1
Readme
Yantra
Serverless tooling built from existing AIS tooling:
- AIS Service Discovery
- Jedlik
Rationale
Currently, we lean heavily on the serverless framework, which is a massive project, and abstracts away some of the need to write Cloudformation. We only use a small sub-set of Serverless's functionality. I.e. registering lambdas with API Gateways etc. This project aims to take some of the functionality we get from Serverless, and replace it work AWS's CDK, and combine it with some of our additional tooling. In order to create an end to end framework for creating serverless apps. With the aim of writing as little cloudformation and in fact yaml as possible.
Leaning more on AWS CDK, means we remove the time spent trying to guess the correct permissions and security rules, as AWS CDK manages the security relationships between your infrastructure components automatically, and to the most optimally secure path.
This would also give us more control over spinning up services and fine-tuning them to our requirements. Because this is just code, just Javascript, we can write plugins, libraries and abstractions, to reduce the amount of configuration and boiler-plate we write.
This is experimental/10% time stuff to validate the idea, so probably don't try to use it for anything yet...
One final key aim is to be able to create a modular environment whereby we can run and test our business logic in multiple configurations. We aim achieve this by transposing the arguments from the various environments, and the return values from business logic, into the same sets of patterns. For example, if I have some business logic, which takes a name and an email as arguments, it should be seemless to switch between a local server, and the Lambda environment for example.
We include a 'transpositions' package, which will extract the required fields from a Lambda event, and pass them into a usecase.
Aims
- Unified way of creating services, with a degree of flexibility.
- Modular approach to configuring and extending services.
- One correct way to do each common task, which can be optimised over time.
- Cutting time spent on boilerplate code and repeatable set-up, utilising code as infrastructure and abstractions.
- Infrastructure and application code in the same language, seamless experience between the two.
- Ability to run locally out of the box.
- Support for HTTP and GraphQL.
- Seamless communication using Cloudmap + AIS Service Discovery as its backbone.
- Less fucking YAML.
- Telemetry, tracing and tooling out of the box.
- Mind blowing developer experience 🤯.
Note: on AppSync - We can actually hook up resolvers and mapping templates etc for AppSycn using the CDK. Which begs the question, to solve the problem we have currently with out AppSync set-up, could we solve that with this: https://docs.aws.amazon.com/cdk/api/latest/docs/aws-appsync-readme.html
Auto-transpose usecases into deliveries
const container = require('./lib');
container.registerUsecase('myUsecase', require('./usecases/my-lambda-handler'));
const handler = container.resolveAsLambda('myUsecase', ['name', 'email']);
Integration with Service Discovery
const container = require('./lib');
container.registerService('myQueue', ServiceTypes.QUEUE, 'latest-namespace.srv.queue');
const handler = ({ myQueue }) => message => {
const { MessageId } = myQueue.queue(message);
return {
MessageId,
};
};
container.registerUsecase('myUsecase', handler);
module.exports = container.resolveAsLambda('myUsecase');
Declerative Infrastructure
Example registering Lambdas to an API Gateway...
class MyApp extends App {
constructor(opts: Opts) {
super(opts);
this.createMessage();
this.getMessage();
}
@http("/api/create-message", ["POST"])
createMessage() {
return this.registerHandler('create-message', './handlers');
}
@http("/api/get-messages", ["GET"])
getMessage() {
return this.registerHandler('get-message', './handlers');
}
@subscribe([
"ais-latest.tenant.onTenantCreated",
"ais-latest.tenant.onNewUser",
])
sendEmail() {
return this.registerHandler('send-email', 'send-email/index.handler');
}
@listen([
'ais-latest.feeds.stream',
])
processFeed() {
return this.registerHandler('process-feed/index.handler');
}
}
const app = new MyApp({
type: 'http',
service: 'my-app',
stage: 'latest',
resource: 'messages',
server: new HttpServer(), // For local use
});
app.deploy(); // Deploys with AWS CDK CLI
app.run(); // Runs locally
Example resource class
The following generates a DynamoDB table from the given Jedlik Entity. If then creates an API Gateway resource, and registers each handler to the correct method.
import {App, Opts} from 'yantra';
import {Feed} from './entities/feed';
@Entity(Feed)
@Resource("feeds")
class MyApp extends App {
constructor(opts: Opts) {
super(opts);
}
@get
getFeed() {
this.registerHandler('./handlers/feeds/get');
}
@post
createFeed() {
this.registerHandler('./handlers/feeds/create');
}
@put
updateFeed() {
this.registerHandler('./handlers/feeds/update');
}
}
To run the above:
$ aws cdk
Example GraphQL (AppSync)
@GraphQL("./graphql")
class MyApp extends App {
constructor(opts: Opts) {
super(opts);
}
@resolver('createUser')
createUserResolver() {
this.registerResolver('./resolvers/create-user');
}
}
const app = new MyApp({
service: 'my-graphql-service',
type: types.GraphQL,
server: new GraphQLServer(),
});