Ứng dụng web sử dụng DynamoDB, Typescript, Express, Generic repository pattern…(Phần 01) – Giới thiệu

Series bài viết này bao gồm các bài viết ngắn ngọn, mô tả và giải thích các bước khi xây dựng một dịch vụ RESTful API, trong đó có sử dụng cơ sở dữ liệu DynamoDB.

Tại sao là DynamoDB?

Bài viết mang mục đích giúp mọi người tiếp cận với DynamoDB thông qua việc xây dựng một ứng dụng web.

DynamoDB là một phần của hệ sinh thái Amazon Web Service, là cơ sở dữ liệu NOSQL, key – value. Thích hợp khi các bạn xây dựng các ứng dụng serverless trên nền tảng AWS.

Có khá nhiều tài liêu về csdl này, nên các bạn có thể tự tìm hiểu.

Generic repository pattern

Repository pattern

Các dự án đòi hỏi việc tách biết các tầng logic, như business, truy xuất cơ sở dữ liệu…

Repository hầu hết được sử dụng những chỗ cần điều chỉnh dữ liệu trước khi truyền xuống tầng data hoặc truyền lên trên business logic.

Lợi ích của việc sử dụng Repository Pattern:

  • Tập trung hóa được các logic về xử lý dữ liệu hoặc business logic.
  • Dễ dàng thực hiện unit test
  • Đưa ra kiến trúc linh hoạt và rõ ràng
  • Khi thay đổi logic của tầng data hoặc business logic, không cần thay đổi Repository

Generic Repository Pattern

Trong đa số trường hợp, dự án cần tạo ra các repository logic tập trung ở một nơi, làm các công việc gần giống nhau  (CRUD) với từng Entity class (các đối tượng, có thể coi mỗi bảng trong csdl là một đối tượng).

Khi áp dụng Generic Repository Pattern, chúng ta gần như sẽ tạo ra được một Repository, mà nó có thể làm việc được với tất cả entity class.

Lợi ích của Generic Repository Pattern:

  • Giảm thiểu sự trùng lặp code
  • Đảm bảo các thành viên trong nhóm phát triển dùng chung 1 pattern
  • Dễ kiểm soát lỗi hơn
  • Dễ dàng bảo trì  và mở rộng sau này

Xây dựng ứng dụng RESTful

Chúng ta bắt đầu đi xây dựng từng phần của ứng dụng.

Trước khi bắt đầu, mình mặc định các bạn có hiểu biết cơ bản về Nodejs, Typescript, NPM.

Khởi tạo project Typescript

Tạo ra thư mục dự án, kể từ đây, nếu không có chú thích đặc biệt thì mặc định chúng ta làm việc trong thư mục này, từ việc tạo file, thư mục con, chạy các lệnh…

Khởi tạo dự án nodejs

Dùng lệnh

npm init -y

Với câu lệnh trên, npm sẽ tạo cho chúng ta file package.json chứa thông tin cơ bản của dự án:

{
  "name": "dynamodb-generic-repository",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
  },
  "keywords": [],
  "author": "hoangdv <hoang.dv@outlook.com> (https://codetheworld.io/)",
  "license": "ISC",
  "devDependencies": {
    "ts-node-dev": "^1.0.0-pre.44",
    "tslint": "^6.0.0",
    "typescript": "^3.8.2"
  }
}

Các bạn chú ý vào phần devDependencies, ở phần này mô tả các thư viện được sử dụng trong quá trình phát triển (chỉ quá trình phát triển). Các bạn có thể thêm chúng bằng 2 cách:

  1. Thêm bằng câu lệnh npm install
    npm install ts-node-dev tslint typescript -D
  2. Cài đặt bằng lệnh npm install
    Các bạn tự sửa nội dung file package.json, thêm các thư viện như trên vào mục devDependencies, rồi gõ lệnh
    npm install

     

Các thư viện trên có ý nghĩa như sau:

  • typescript: Dùng để dịch mã Typescript sang Javascript.
  • tslint: Định nghĩa, kiểm tra, sửa code convention.
  • ts-node-dev: Một phiên bản mở rộng cho ts-node, giúp chúng chúng ta có thể chạy trực tiếp code Typescript. Thư viện này làm việc cũng giống nodemon, khi các file được cập nhật, tiến trình sẽ được khởi động lại.

Chuẩn bị các cài đặt cho Typescript

Vì dự án sử dụng Typescript, nên chúng ta cần tạo ra file cấu hình cho việc chuyển đổi từ Typescript sang Javascript.

Dùng lệnh

./node_modules/.bin/tsc --init

Câu lệnh trên tạo ra file  tsconfig.json , cấu hình cho Typescript trong dự án.

{
  "compilerOptions": {
    /* Basic Options */

    "target": "ES2018", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
    "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
    "sourceMap": true, /* Generates corresponding '.map' file. */
    "outDir": "./.dist", /* Redirect output structure to the directory. */
    "rootDir": "./src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
    /* Strict Type-Checking Options */
    "strict": true, /* Enable all strict type-checking options. */
    /* Module Resolution Options */
    "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
    "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
  }
}

Chú ý tới một số cài đặt quan trọng:

  • target: Mã Javascript được tạo ra từ mã Typescript sẽ tương thích với phiên bản ES nào. Mình dùng nodejs version 11, nên mình sẽ để là ES2018
  • module: Cách chúng ta export và import các module
  • sourceMap: Hỗ trợ việc debug
  • outDir: Thư mục chứa mã Javascript sẽ gen ra
  • rootDir: Thư mục sẽ chứa code Typescript của dự án
  • moduleResolution: Cho biết chúng ta làm việc trên môi trường Nodejs

Các thông số khác các bạn có thể tự tìm hiểu 🙂

TSLint

Tạo ra file định nghĩa code convention sẽ dùng trong dự án.

./node_modules/.bin/tslint --init

Lệnh trên sẽ tạo ra file tslint.json , chúng ta sẽ định nghĩa một số luật cơ bản trước, sau này sẽ bổ sung thêm khi thấy cần thiết:

{
    "defaultSeverity": "error",
    "extends": [
        "tslint:recommended"
    ],
    "jsRules": {},
    "rules": {
        "quotemark": [true, "single"],
        "import-spacing": true
    },
    "rulesDirectory": []
}

Hello Typescript

Chúng ta tạo ra thư mục con “src“, code của dự án sẽ được chứa trong thư mục này.

Trong thư mục src, chúng ta tạo file index.ts

(async () => {
  await new Promise(resolve => {
    setTimeout(resolve, 1000);
  });
  // tslint:disable-next-line:no-console
  console.log('Hello TS!');

  // Keep running
  setInterval(() => {/**/}, 1000);
})();

Đoạn code khá đơn giản, chờ 1 giây, in ra màn hình dòng chữ Hello TS!

Biên dịch và chạy: 

./node_modules/.bin/tsc

node ./.dist/index.js

Với cách này, mỗi lần sửa nội dung file index.ts, chúng ta phải chạy lại 2 lệnh trên để thấy sự thay đổi.

Hoặc sử dụng ts-node, chạy trực tiếp file .ts

./node_modules/.bin/ts-node ./src/index.ts

Lệnh này thì tiện hơn chút, không cần phải chạy lệnh build.

Nhưng để tiện cho quá trình phát triển, chúng ta sẽ tạo ra cách script, chúng sẽ là shortcut cho các lệnh hay dùng. Trong file package.json, block scripts, chúng ta cập nhật thêm nội dung như sau:

"scripts": {
   "test": "echo \"Error: no test specified\" && exit 1",
   "build": "./node_modules/.bin/tsc",
   "prestart": "npm run build",
   "start": "node ./.dist/index.js",
   "dev": "./node_modules/.bin/ts-node-dev ./src/index.ts",
   "lint": "./node_modules/.bin/tslint --project .",
   "lint:fix": "./node_modules/.bin/tslint --project . --fix"
 },

Các script có nội dung:

  • build: Build project
  • prestart: Trước khi thực hiện lệnh start, lệnh này sẽ được chạy trước
  • start: Chạy file index.js
  • dev: Chạy file src/index.ts và lắng nghe các file, nếu có thay đổi thì khởi động lại tiến trình
  • lint: Tìm các lỗi code convention
  • lint:fix: Tìm các lỗi code convention và tự động sửa

Trong lúc phát triển, chúng ta sẽ dùng lệnh: 

npm run dev

 

IDE

Các bạn có thể sử dụng bất kỳ text editor nào để phát triển dự án. Nhưng mình khuyên sử dụng những IDE chuyên cho việc phát triền Javascript hay Typescript, như VSCode, Webstorm. Với những IDE này, việc phát triển sẽ dễ dàng hơn rất nhiều, từ việc code suggestion, refactor, lint (cấu hình TSLint cho IDE)…

Kết thúc phần 01

Phần 01 chỉ có thế, chuẩn bị cho việc phát triển dự án: Công cụ, các file cơ bản…

Ở phần sau, chúng ta sẽ cài đặt DynamoDB ở phía local, tạo bảng, load trước data mẫu…

Đây là project sau khi kết thúc phần 01: Github