[Node.js]Node.js+ExpressでMVCっぽく書いてみる

2019.04.25

 

 

こんにちは千田です。

 

さて、今回はタイトルの通り「Node.js+ExpressでMVCっぽく」書いてみようと思います。

初めてExpressに触れた時、ほとんどのコードをrouterに直接書いてしまっていて、もう少しうまく書けないかなーと思った事がきっかけです。

 

そもそもMVCモデルとは

アプリケーションをM(Model)V(View)C(Controller)の3つに分割して構築するお作法の事です。

以下、 Wikipediaより

 

・Model:アプリケーションデータ、ビジネスルール、ロジック、関数

・View:グラフや図などの任意の情報表現

・Controller:入力を受け取りmodelとviewへの命令に変換する

Expressとは

公式サイトより

Express は、Web アプリケーションとモバイル・アプリケーション向けの一連の堅固な機能を提供する最小限で柔軟な Node.js Web アプリケーション・フレームワークです。

 

上記の通り、Expressはあくまで最小限な機能を提供するものなので、モジュールを組み合わせて柔軟に構築する事ができます。

 

よく見る書き方

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
var express = require('express');
var router  = express.Router();
var mysql   = require('mysql');
var db      = require('../config/db');

router.get('/users/index', (req, res, next) => {

  var sql = 'select * from users';

  try {
    const connection = mysql.createConnection(db);
    connection.connect();
    connection.query(sql,
      (error, results, fields) => {
        if (error == null) {
          res.render('index.ejs',
          {
            content: results
          });
        }else{
          throw(error);
        }
      }
    );
    connection.end();
  } catch(e) {
    res.render('index.ejs',
    {
      content: {error: e},
    });
  }
});

Expressで書かれたコードを調べている時、よく見かけるのはこんな感じのコードかと思います。

当然、上記のコードでも実行する上で問題は無いのですが、量が増えてくるとコードの見通しが悪くなりそうです。

他の言語なんかでMVCフレームワークを用いた開発をした事がある方には違和感もあるのかなと。

千田も元々CakePHPとかをやっていたクチなので、もう少し整理できないものかなーと思いました。

 

やりたい事

Expressを使っていわゆるMVCフレームワークっぽく書いてみたい。

 

やってみる

という事で早速書いていきます。今回はユーザー情報を表示するページを想定して書いてみます。

 

前提条件

・Node.jsのバージョン: 10.15.1

・Expressのバージョン: 4.16.0

・データベース: MySQL 5.7

 

ディレクトリ構成

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
├── app.js
├── bin
│   └── www

├── config
│   └── db.js

├── controllers
│   └── UserController.js

├── models
│   └── Users.js

├── views
│   └── index.ejs

├── routes
│   └── routes.js

├── package.json
├── public

express-generatorで初期構築したあとに

・config

・controllers

・models

を追加しています。

 

ルーティング設定

routes/routes.js

1
2
3
4
5
6
7
8
var express = require('express');
var router = express.Router();
var userController = require('../controllers/UserController')

router.get('/users', userController.doGetUser);
router.post('/users/regist', userController.doRegistUser);

module.exports = router;

ルーティングはroutes.jsに書いています。

あくまでHTTPリクエストのエンドポイントに対して、どのモジュールで処理をするかだけを書くようにしています。

3行目でいわゆるcontrollerを読み込んでいます。

 

Controllerを書く

controllers/UserController.js

1
2
3
4
5
6
7
8
9
10
11
const express = require('express');
const Users = require('../models/Users');
const Views = '../views/'

module.exports = {
  doGetUser: function (req, res, next) {
    Users.getUser(id).then((result) => {
      res.render(Views + 'index.ejs',{users: result});
    });
  }
}

Controllerではrouterから受け取ったリクエストに対してModelでの処理を実行し、結果をViewに渡しています。

2行目でModelを読み込んでいます。

 

Modelを書く

models/User.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const mysql = require('mysql');
const db = require('../config/db');
const table = 'users';

module.exports = {

  getUser: function () {
    return new Promise ((resolve, reject) => {
      const con = mysql.createConnection(db);
      con.query(
        `select id, name, date_format(birthdate,"%Y/%m/%d") as birthdate from ${table}`,  (err, result, fields) => {
          if ( err ) {
            reject(err);
          } else {
            resolve(result);
          }
        });
      con.end();
    });
  },
}

ModelではDBから値を取得し、Controllerにそれを返しています。

 

Viewを書く

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<table>
  <tbody>
    <th>No</th>
    <th>名前</th>    
    <th>生年月日</th>
    <% for( var i in users ){ %>
    <tr>
      <td><%= users[i].id %></td>
      <td><%= users[i].name %></td>
      <td><%= users[i].birthdate %></td>
    </tr>
    <% } %>
  </tbody>
</table>

Controllerから受けたデータを用いてレンダリングします。

 

まとめ

もっといい書き方はあるかと思いますが、上記のようにMVCっぽく分ける事はできたかなと思います。

routerに全ての処理を書いていた時と比べると非常にスッキリしたと思いますし、メンテナンスもしやすくなるのかなと思います。

 

という訳で、個人的には「Node.js+ExpressでMVCっぽく書いてみる」ことができたと思います。

 

それではまた!


Top