在express 專案內 app.js 扮演整個主程式關鍵的 server controller 角色,我們也把許多 logic 定義在這邊,但不免顯得雜亂,上一篇 Express 內建路由器介紹 主要是在講如何用 express.Routers 以及 Routes directory 根據Restful API 設計原則將網頁路徑進行重構。

而對於專案的資料庫使用而言,多個檔案都會用到的資料庫邏輯一樣會有這樣子關注點分離管理的需求,這篇主要就是同樣利用 todo list 專案來介紹怎麼樣整理專案內的 Mongoose 連線邏輯。

由於 mongoose 連線功能在分類上較偏向於專案 環境設定 (configuration) 因此我們在專案根目錄下再開一個 config 資料夾,裡面新增一個 mongoose.js 檔案,主管 mongoose 套件相關的連線設定邏輯,可以預期這樣的共用連線邏輯也會是 export 成為一個 module ,讓各個專案內需要使用 Mongo DB 連線的檔案都可以利用這個 MOD

以下是 /config/mongoose.js 檔案內容,基本上把原本在 app.js 內與 DB 相關的邏輯都搬過來放:

// 檔案: /config/mongoose.js
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/todo-list', { useNewUrlParser: true, useUnifiedTopology: true });

	const db = mongoose.connection;

db.on('error', () => {
  console.log('mongodb error');
})
db.once('open', () => { 
  console.log('mongodb connected');
})

module.exports = db;

而在 app.js 只需要加一行引用 /config/mongoose.js 就好,整理過後如下:

// import npm(現成)套件
const express = require('express');
const exphbs = require('express-handlebars');
const bodyParser = require('body-parser');
const methodOverride = require('method-override');
// import 自製套件
const routes = require('./routes');
require('./config/mongoose');

// 建立 Server 變數 app
const app = express();

// 各種設置 & middleware
app.engine('hbs', exphbs({ defaultLayout: 'main', extname: '.hbs' }));
app.set('view engine', 'hbs');
app.use(bodyParser.urlencoded({ extended: true }));
app.use(methodOverride('_method'));

// routing
app.use(routes);

// server listen
app.listen(3000, () => {
  console.log('Sever is listening in port 3000.');
})

可以發現 Server controller app.js 在經過 routes重構 & DB連線設定重構 後乾淨整潔了非常非常多,功能邏輯也都清晰清楚。

可以利用同樣的邏輯來對產生種子資料的 /modles/seeds/todoSeeder.js 檔案 來進行重構,因為裡面也跟 /config/mongoose.js 用了同樣的資料庫連線設定,這樣以後需要用到時就不需要改兩個檔案(例如連線DB網址有更動之類的)。以下是原本的 todoSeeder.js 檔案:

const mongoose = require('mongoose');
const Todo = require('../todo');  

mongoose.connect('mongodb://localhost/todo-list', { useNewUrlParser: true, useUnifiedTopology: true });

const db = mongoose.connection;

db.on('error', () => {
  console.log('mongodb error');
});

db.once('open', () => { 
  console.log('mongodb connected');

  for (let i =0; i < 10; i++) {
    Todo.create({ name: `name-${i}`});
  }
  
  console.log('done.');
});

可以看到他跟 mongoose.js 檔案作的事並不完全一樣,還是有些微差異性,主要是 db.once() 內的操作內容不同。而由於我們剛剛在 mongoose.js 有將其 export 成一個變數 db; 跟 app.js export後不用變數接不同,這次操作可以在 todoSeeder.js 先用一樣變數 db 接住 import 進來的 mongoose.js ,再將其以 db.once() 啟動。

其中 db.once() 裡面就可以 callback function 來操作 todoSeeder.js 需要的獨有功能邏輯。

作法如下:

// 刪除重複的部分,引入 **mongoose.js** 作為變數 **db。啟動** db.once() 後將**todoSeeder.js** 
// ****獨有的部分放到 db.once() 內作為 callback function

// 這邊是要 import models/todo 內定義的 Todo schema, 不能刪 
const Todo = require('../todo');  

// import /config/mongoose ; 令為變數 db
const db = require('../../config/mongoose');

// 運行db 並將 todoseeder.js 的邏輯放到 callback function 裡
db.once('open', () => { 
  for (let i =0; i < 10; i++) {
    Todo.create({ name: `name-${i}`});
  }
  
  console.log('done.');
});