iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 17
0
Software Development

今晚我想來點 Express 佐 MVC 分層架構系列 第 17

[今晚我想來點 Express 佐 MVC 分層架構] DAY 17 - Model

  • 分享至 

  • xImage
  •  

Model 就是負責處理資料與業務邏輯的地方,我們使用的資料庫為 MongoDB,並用 mongoose 套件來輔助設計,所以會依照 mongoose 的設計方法來實作 Model。

建立 Database 物件

要使用資料庫就必須進行連線,這邊我們就建立一個名為 database 的資料夾,並包含了 database.tsdatabase.option.ts 以及 index.ts 這三個檔案,當前的資料夾結構:

├── src
|   ├── index.ts
|   ├── app.ts
|   ├── app.routing.ts
|   ├── + bases
|   ├── + common/resonse
|   ├── + exceptions
|   ├── + main/api
|   ├── + types
|   ├── + environments
|   ├── database                 //本篇新增
|   |   ├── index.ts             //本篇新增
|   |   ├── database.ts          //本篇新增
|   |   └── database.option.ts   //本篇新增
|   └── + validators
├── package.json
└── tsconfig.json

下方為 database.option.ts 的程式碼:

export const DATABASE_OPTIONS = {
  useNewUrlParser: true,
  useUnifiedTopology: true
};

下方為 database.ts 的程式碼:

import mongoose from 'mongoose';
import { DATABASE_OPTIONS } from './database.option';

export class Database {

  public connect(): void {
    mongoose.connect(
      `mongodb+srv://${ process.env.DB_USER }:${ process.env.DB_PWD }@expressmvctodolist-clus.jjknt.gcp.mongodb.net/${ process.env.DB_NAME }?retryWrites=true&w=majority`,
      DATABASE_OPTIONS
    )
    .then(() => console.log(`Database ${ process.env.DB_NAME } is connected.`))
    .catch(err => console.error(err));
  }

}

最後透過 index.ts 來統一管理:

export * from './database';

與 MongoDB 連線

App 新增 launchDatabase() 並引入 Database 以及輸入下方程式碼:

import { Database } from './database';
public launchDatabase(): void {
  const database = new Database();
  database.connect();
}

並在 index.ts 中呼叫:

import { App } from './app';
import { DefaultException } from './exceptions/default.exception';

const bootstrap = () => {
  const app = new App();
  app.setException(DefaultException);
  app.launchDatabase();                 // 這裡
  app.bootstrap();
};

bootstrap();

建立 Model

我們先建立一個名為 models 的資料夾,並包含 todo.model.ts,還記得 mongoose 在設計 Model 時要先做什麼事嗎?沒錯,要建置 Schema,我們先在 todo.model.ts 裡設計 Schema:

import { model, Schema } from 'mongoose';

const TodoSchema = new Schema(
  {
    content: {
      type: String,
      required: true
    },
    completed: {
      type: Boolean,
      required: true
    }
  },
  {
    timestamps: true
  }
);

timestamps 是啟用 createdAtupdatedAt 的配置

接著要建立 Model:

export const TodoModel = model('Todo', TodoSchema);

這樣就完成建立 Model 了,但在使用時無法知道有哪些欄位,因為 model() 預設是定義輸出 Document 型別,這時候我們就要寫 Interface 來處理這個問題,考量到還有共用欄位 createdAtupdatedAt,所以在 types 資料夾下建立 model.type.ts

import { Document } from 'mongoose';

export interface CoreDocument extends Document {
  createdAt: Date;
  updatedAt: Date;
}

我們再回到 todo.model.ts,在這裡建立 TodoModel 的 Interface,並繼承 CoreDocument

import { CoreDocument } from '../types/model.type';

export interface TodoDocument extends CoreDocument {
  content: string;
  completed: boolean;
};

最後就是指定 model() 定義的型別,整個 todo.model.ts 如下:

import { model, Schema } from 'mongoose';
import { CoreDocument } from '../types/model.type';

const TodoSchema = new Schema(
  {
    content: {
      type: String,
      required: true
    },
    completed: {
      type: Boolean,
      required: true
    }
  },
  {
    timestamps: true
  }
);

export interface TodoDocument extends CoreDocument {
  content: string;
  completed: boolean;
};

export const TodoModel = model<TodoDocument>('Todo', TodoSchema);

當前資料夾結構如下:

├── src
|   ├── index.ts
|   ├── app.ts
|   ├── app.routing.ts
|   ├── + bases
|   ├── + common/resonse
|   ├── + exceptions
|   ├── + main/api
|   ├── models
|   |   └── todo.model.ts        //本篇新增
|   ├── types
|   |   ├── model.type.ts        //本篇新增
|   |   └── response.type.ts
|   ├── + environments
|   ├── database                 //本篇新增
|   |   ├── index.ts             //本篇新增
|   |   ├── database.ts          //本篇新增
|   |   └── database.option.ts   //本篇新增
|   └── + validators
├── package.json
└── tsconfig.json

小結

mongoose 本身就有提供 Model,我們只需要在上面擴展即可,十分方便。下一篇將會統合 MVC,製作一個簡單的 TodoList API Server,敬請期待!


上一篇
[今晚我想來點 Express 佐 MVC 分層架構] DAY 16 - Controller 與 Exception
下一篇
[今晚我想來點 Express 佐 MVC 分層架構] DAY 18 - 整合 Express MVC
系列文
今晚我想來點 Express 佐 MVC 分層架構30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
tim80411
iT邦新手 5 級 ‧ 2023-03-11 21:31:50

不確定是不是版本造成的差異:在mongoose 7的時候,有一個 automatic type inferrence

能解決和上文中相同的問題:

這樣就完成建立 Model 了,但在使用時無法知道有哪些欄位

所以看起來其實不用建立TodoDocument就可以解決這個問題,於是也在想,額外建立TodoDocument會有其他的好處有助於維護程式碼嗎?

或是其實可以直接使用上述automatic type inferrence就可以了?

HAO iT邦研究生 3 級 ‧ 2023-03-19 11:43:02 檢舉

你好,在新版本的 mongoose 確實有自動型別推論的功能,若用起來沒問題可以直接使用,不過如果有問題,官方建議先回到本來的做法。

Mongoose:

If automatic type inference doesn't work for you, you can always fall back to document interface definitions.

我要留言

立即登入留言