Express 項目構造最好實踐(下)

Models 是你與你的數據庫交互的一些文件。它們包括了你處置懲罰你的數據的一切要領和功用。它們不單單議包括了建立、讀取、更新和刪除的要領,還包括了營業邏輯。比方,假如你有一個 car model,你可以有一個 mountTyres 要領。

在你的數據庫中,針對每種範例的數據,你應當建立最少一個文件。在我們的例子中,我們有 users 和 comments,因而我們有 user model 和 comment model。有時刻,當一個 model 文件很大,更好的做法是基於內部的邏輯將這個 model 文件分紅好幾個文件。

你應當讓你的 models 自力於外部。models 之間不應當互相援用。它們不須要知道哪一個 controller 挪用它們。它們永久不要吸收 request 或 reponse 對象,它們永久不要返回 http 的毛病,然則它們應當返回 model 的毛病。

一切的這些將會使你的 models 更好保護。由於它們是自力的,所以可以很好地測試它們。Models 可以挪動就任何須要用到它的處所。轉變一個 model,不會應當其他的東西,由於它是自力的。

基於上面提的的點,讓我們來看看怎樣完成我們例子中的 model。下面是 comment model。

var db = require('../db')

// Create new comment in your database and return its id
// 在你的數據庫中建立一條新的 comment 
exports.create = function(user, text, cb) {
  var comment = {
    user: user,
    text: text,
    date: new Date().toString()
  }

  db.save(comment, cb)
}

// Get a particular comment
exports.get = function(id, cb) {
  db.fetch({id:id}, function(err, docs) {
    if (err) return cb(err)
    cb(null, docs[0])
  })
}

// Get all comments
exports.all = function(cb) {
  db.fetch({}, cb)
}

// Get all comments by a particular user
exports.allByUser = function(user, cb) {
  db.fetch({user: user}, cb)
}

user model 沒有包括進來。comment model 不體貼它是什麼,它僅僅體貼它怎樣存儲。

var db = require('../db')
  , crypto = require('crypto')

hash = function(password) {
  return crypto.createHash('sha1').update(password).digest('base64')
}

exports.create = function(name, email, password, cb) {
  var user = {
    name: name,
    email: email,
    password: hash(password),
  }

  db.save(user, cb)
}

exports.get = function(id, cb) {
  db.fetch({id:id}, function(err, docs) {
    if (err) return cb(err)
    cb(null, docs[0])
  })
}

exports.authenticate = function(email, password) {
  db.fetch({email:email}, function(err, docs) {
    if (err) return cb(err)
    if (docs.length === 0) return cb()

    user = docs[0]

    if (user.password === hash(password)) {
      cb(null, docs[0])
    } else {
      cb()
    }
  })
}

exports.changePassword = function(id, password, cb) {
  db.update({id:id}, {password: hash(password)}, function(err, affected) {
    if (err) return cb(err)
    cb(null, affected > 0)
  })
}

除了建立和治理用戶所須要的功用以外,那還有用於用戶身份驗證和暗碼治理的要領。再一次的,這個 model 不知道已存在的其他的 model、controller 或許運用的其他部份。

Views

這個文件夾包括了你運用一切須要襯着的模板。一般,團隊中的設計師會在這裏事情。

你想每一個 controllers 所對應的模板都有一個子文件夾。如許的話,你將會為雷同的使命組合模板。

挑選一個模板言語會讓人疑心,由於有許多的挑選。我們最喜歡的模板言語,是 Jade 和 Mustache,我們一向在用。Jade 很合適天生 html 頁面。它使得寫 html 標籤更短和越發可讀。針關於前提和迭代,它也可以運用 JavaScript。Mustache 在別的一方面,專註於襯着林林總總的模板,它供應了盡量少的邏輯運算符而且處置懲罰數據的要領很少。這使得它異常合適編寫異常清潔的模板,這些模板專註於顯現你的數據而不是處置懲罰數據。

寫好一個模板的最好實踐是防止在模板中做任何處置懲罰。假如你的數據須要在顯現之前舉行處置懲罰,在你的 controller 中處置懲罰。也要防止增加太多的邏輯,尤其是這個邏輯可以被移至 controller。

doctype html
html
  head
    title Your comment web app
  body
    h1 Welcome and leave your comment
    each comment in comments
      article.Comment
        .Comment-date= comment.date
        .Comment-text= comment.text

如你所見,在襯着這個模板時,數據估計已被處置懲罰好了。

Controllers

這是一個文件夾,你將會定義你運用一切的路由在這個文件夾中。你的 controllers 將會處置懲罰 web 要求,將模板供應給用戶,而且和你的 models 舉行交互,以處置懲罰和檢索數據。這是膠水,可以銜接和掌握你的 web 運用。

一般,關於你運用中的每一個邏輯部份,你最少會有一個文件。比方,一個文件處置懲罰批評,別的一個文件處置懲罰關於用戶的要求等等。來自同一個 controller 的一切路由都有雷同的前綴,這是一個好的實踐。比方 /comments/all 和 /comments/new。

有時刻很難決議什麼應當進入 controller,什麼應當進入 model。一個最好的實踐是應當永久不會直接接見數據庫。它永久不應當挪用 write,update,fetch 這些數據庫供應的要領,而應當依託 model 中的要領。比方假如你有一個 car model,你想要把 4 個輪子安裝到這個 car 上,controller 不會挪用 db.update(id, { wheels: 4 }),而是會挪用像 car.mountwheels(id, 4) 如許的要領。

下面是擔任批評的 controller。

var express = require('express')
  , router = express.Router()
  , Comment = require('../models/comment')
  , auth = require('../middlewares/auth')

router.post('/', auth, function(req, res) {
  user = req.user.id
  text = req.body.text

  Comment.create(user, text, function (err, comment) {
    res.redirect('/')
  })
})

router.get('/:id', function(req, res) {
  Comment.get(req.params.id, function (err, comment) {
    res.render('comments/comment', {comment: comment})
  })
})

module.exports = router

在 controller 文件夾中,也有一個 index.js 文件夾。它的目標是加載一切其他的 controllers,和能夠定義一些沒有雷同前綴的途徑,比方 home 頁面路由。

var express = require('express')
  , router = express.Router()
  , Comment = require('../models/comment')

router.use('/comments', require('./comments'))
router.use('/users', require('./users'))

// 與 comments 和 users 差別的是,home 頁面不須要前綴(comments 或 users)
router.get('/', function(req, res) {
  Comments.all(function(err, comments) {
    res.render('index', {comments: comments})
  })
})

module.exports = router

這個文件將會處置懲罰你一切的路由。你的運用在啟動的必需加載的唯一的路由器。

Middlewares

在這個文件夾中,你將會存儲一切你 Express 的中間件。中間件的目標是為了提取罕見的 controller 代碼,它將會在多個要求中實行,而且一般會修正 要求/相應 對象。

就像一個 controller,一个中間件永久不應當接見數據庫,相反,關於它要完成的每一項使命,它應當運用你的 models。

下面是一個 users 中間件,來自 middlewares/users.js 文件。它的目標是加載發出要求的用戶。

User = require('../models/user')

module.exports = function(req, res, next) {
  if (req.session && req.session.user) {
    User.get(req.session.user, function(err, user) {
      if (user) {
        req.user = user
      } else {
        delete req.user
        delete req.session.user
      }

      next()
    })
  } else {
    next()
  }
}

這个中間件運用 user model,而且它沒有直接接見數據庫。

下一步,authorization 中間件,當你想要阻撓沒有權限接見雷同路由的時刻,可以用到這个中間件。

module.exports = function(req, res, next) {
  if (req.user) {
    next()
  } else {
    res.status(401).end()
  }
}

它沒有任何外部的依靠。假如你看看上面的 controllers 文件,你可以看看怎樣它是怎樣運用的。

Helpers

這個文件夾包括有用的代碼,這些代碼被用在多個 models,middlewares 或許 controllers 中,然則 helpers 不屬於 models,middlewares 或 controllers 的領域。一般,你對差別的罕見使命,會有差別的文件。

一個例子就是 helper 文件供應一些要領來治理日期和時候。

Public

這個文件件只是供應靜態文件。一般,它會有子文件夾,像 css,libs,img 用於 css 款式,圖片和 JavaScript 庫就像 jQuery。這個文件夾可以供應效勞的最好實踐不是經由過程你的運用,而是經由過程一個 Nginx 或許 Apache 效勞,它們比起 Node 在靜態文件的效勞更好。

Tests

每一個項目都須要測試,而且你須要將一切的測試群集到一同。為了協助治理它們,你將它們星散在差別的子文件中。

  • controllers
  • helpers
  • models
  • middlewares
  • integration
  • ui
    原文作者:hileix
    原文地址: https://segmentfault.com/a/1190000015367777
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞