This article is a translation of the original article.
I implemented an empty API of sample prepared in the previous article*1 so that it works.
Specification of API
I created todo's REST API.
POST /todos
: Register todoGET /todos
: List todosGET /todos/:id
: Get todoPUT /todos/:id
: Update todoDELETE /todos/:id
: Delete todo
Architecture
Final goal (but I can not get it...)
I tried to make it to Clean Architecture.
Clean Architecture is an architecture that attempts to improve maintainability by separating abstract implementations (business logic etc.) from concrete implementation (framework, DB, etc.) as follows.
- Divide the system into four layers (
Entities
,Use Cases
,Interface Adapters
,Frameworks and Drivers
) - Make dependency between layers one-way from concrete layer to abstract layer (
Frameworks and Drivers
->Interface Adapters
->Use Cases
->Entities
)
If the dependencies between the layers do not match the order of the calls, it is necessary to adjust the direction of dependency between the layers by making use of the dependency inversion principle.
The figure is easier to understand than the explanation. (The figure quoted The Clean Architecture.)
This article
In the architecture design of this article, I did not follow all the principles of Clean Architecture, but we adopted only the purpose / way of thinking that improves the maintainability of the abstract layer by one way from concrete layer to abstract layer. Because if follow all principles that implementation will increase too much and disadvantage will increase.
Specifically, I broke the rules as follows and obeyed the principle only dependency between layers one-way from Interface Adapters, Frameworks and Drivers
to Entities and Use Cases
.
Interface Adapters
is a layer that maps concrete layers (Frameworks and Drivers
) and abstract layers (Entities and Use Cases
). And this layer may depend on all layers.
The direction of the arrow between Interface Adapters
andUse Cases
is all inward.
You can probably reuse business logic even if you want to change Framework, DB etc.
Implementation
I uploaded the code to GitHub.
The directory structure is as follows.
$ tree -ad -I '.git' . ├── .circleci ... config of CircleCI ├── app ... application codes │ ├── adapters ... layer of Interface Adapters │ │ ├── commands │ │ ├── databases │ │ └── http │ │ ├── requests │ │ └── responses │ ├── commands ... layer of Frameworks and Drivers(command) │ ├── databases ... layer of Frameworks and Drivers(DynamoDB) │ ├── entities ... layer of Entities │ ├── http ... layer of Frameworks and Drivers(Serverless Framework) │ │ ├── controllers │ │ ├── middlewares │ │ ├── models │ │ ├── utils │ │ └── views │ ├── providers ... setting of DI (inversify) │ └── usecases ... layer of Use Cases │ ├── implementations │ ├── inputs │ ├── outputs │ └── stores ├── dockers ... config of docker │ ├── dynamodb │ └── serverless └── test ... test codes ├── units │ ├── http │ │ ├── controllers │ │ └── middlewares │ └── usecases └── utils 33 directories
In implementation of this article, I used middy for http's middleware engine. The middleware directory is app/http/middlewares/
.
aws-serverless-express and serverless-http were also candidates. However, I tried using middy which is specialized in middleware so as not to use two routing frameworks of Serverless Framework
and Express
.
middy
is still alpha version so it may not work with production code, but I felt it was fairly easy to use.
Also, by checking with tslint, we made it comply with tslint-config-standard
. And I tried to write a unit test with mocha and keep it clean.
How to use
There are two ways to run it in the local environment using docker or to run it with AWS.
Using docker
Clone the Repository:
$ git clone -b v0.0.1 https://github.com/nihemak/aws-sls-spa-sample-api.git sample-spa-api $ cd sample-spa-api
Build docker environment and Reset DynamoDB Local tables:
$ npm run docker-build $ npm run docker-up-dev $ npm run docker-reset-tables $ npm run docker-down
Invoke serverless-offline in docker environment:
$ npm run docker-up $ npm run docker-offline
Try the APIs:
# POST /todos $ curl -X POST http://localhost:3000/v1/todos -H "Content-Type: application/json" --data '{ "text": "foo" }' # GET /todos $ curl http://localhost:3000/v1/todos # GET /todos/:id $ curl http://localhost:3000/v1/todos/:id # PUT /todos/:id $ curl -X PUT http://localhost:3000/v1/todos/:id -H "Content-Type: application/json" --data '{ "text": "bar", "checked": true }' # DELETE /todos/:id $ curl -X DELETE http://localhost:3000/v1/todos/:id
Access to DynamoDB Local admin web:
With AWS
It is as the How to use of the previous article.
But the implementation of this article is tag v.0.0.1. Therefore, you need to change only the tag part.
Specifically, the place to Clone the Repository and Push to the AWS CodeCommit Repository will change to the following.
# spa infra repository... $ git clone -b v0.0.1 https://github.com/nihemak/aws-sls-spa-sample-terraform.git sample-spa-infra $ cd sample-spa-infra $ git checkout -b master $ git push ssh://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/foobar-sample-spa-infra $ cd .. # spa api repository... $ git clone -b v0.0.1 https://github.com/nihemak/aws-sls-spa-sample-api.git sample-spa-api $ cd sample-spa-api $ git checkout -b master $ git push ssh://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/foobar-sample-spa-api $ cd .. # spa web repository... $ git clone -b v0.0.0 https://github.com/nihemak/aws-sls-spa-sample-web.git sample-spa-web $ cd sample-spa-web $ git checkout -b master $ git push ssh://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/foobar-sample-spa-web $ cd ..
The repositories of aws-sls-spa-sample-terraform.git
and aws-sls-spa-sample-api.git
will be v0.0.1
.
Summary
In this article, I made a sample REST API to manage Todo with Serverless Framework and TypeScript.
- I was able to understand the concept and implementation cost of Clean Architecture.
- I was able to learn TypeScript coding.