"保持简单, 保持模块化."
开发步骤
通常是从画一个项目的草图开始. :
- 分析项目的草图, 理解需要的Domain
- 创建项目的README文件
- 基于草图, 在文档中写出路由和 API
- 创建领域模型
- 选择软件堆栈和要依赖的Module
- 设置项目的仓储
- 写出数据库scheme, 创建数据库
- 开始编码,写Models 和 Collections
- 写单元测试
- 写Controllers 和类库
- 创建路由
- 写集成测试
- 创建API
- Review代码, 有必要的话进行调整
架构
我的应用受MVC的影响比较大. MVC 架构非常适合Node.js 开发.
下面是我的典型的目录结构.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
/
api/
bin/
collections/
config/
controllers/
env/
lib/
models/
public/
routes/
test/
views/
.gitignore
.jshintrc
app.js
package
.json
README.md
|
下面我们描述下每个文件夹和文件在我们项目目录里的用处.
Documentation (./README.md)
README.md是我项目里非常重要的一个文件
.
我的 README.md
文件包含下面的信息:
- 项目名和描述
- 软件要求
- 依赖
- Getting started instructions
- 需求配置
- 任务命令
- 风格指南
- 应用架构
- 路由/API
- License信息
下面的例子是我怎么描述我的路由的:
1
2
3
4
5
6
|
/**
* Routes
**/
GET /items - get a collection of items
GET /items/:id - get one item
POST /items - save an item
|
./models
在软件应用中, model通常代表一个数据库表的记录.
./models/mymodel.js
1
2
3
4
5
6
7
8
9
10
|
// get config
var
config = require(
'../config'
);
// connect to the database
var
Bookshelf = require(
'../lib/dbconnect'
)(config);
// define model
var
myModel = Bookshelf.Model.extend({
tableName:
'items'
});
// export collection module
module.exports = myModel;
|
./collections
Collections 像表一样的一组model. 一个collection
通常代表一个完整的数据库表.
./collections/mycollection.js
1
2
3
4
5
6
7
8
|
//require the model for this collection
var
myModel = require(
'../models/mymodel'
);
// define collection
var
myCollection = Bookshelf.Collection.extend({
model: myModel
});
// export collection module
module.exports = myCollection;
|
./controllers
Controllers, 和其他的典型的 MVC 一样, 负责应用的业务逻辑. 我们的controllers根据路由处理数据、查询数据库.
./controllers/items.js
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
33
34
35
36
37
38
39
40
|
var
myModel = require(
'../models/mymodel'
);
var
myCollection = require(
'../collections/mycollection'
);
module.exports = {
// GET /items/:id
getItem:
function
(req, res, next) {
var
id = req.params.id;
myModel.forge({id: id})
.fetch()
.then(
function
(model) {
res.json(model.toJSON());
})
.otherwise(
function
(error) {
res.status(500).json({msg: error.message});
});
},
// GET /items
getItems:
function
(req, res, next) {
var
id = req.params.id;
myCollection.forge()
.fetch()
.then(
function
(collection) {
res.json(collection.toJSON());
})
.otherwise(
function
(error) {
res.status(500).json({msg: error.message});
});
},
// POST /items
// (Don't forget to validate and sanitize all user input)
saveItem:
function
(req, res, next) {
myModel.forge(req.body)
.save()
.then(
function
(model) {
res.json(model.toJSON());
})
.otherwise(
function
(error) {
res.status(500).json({msg: error.message});
});
}
};
|
./routes
存放路由.
./routes/items.js
1
2
3
4
5
6
7
8
9
|
var
express = require(
'express'
);
var
itemsController = require(
'../controllers/items'
);
module.exports =
function
() {
var
router = express.Router();
router.get(
'/items'
, itemsController.getItems);
router.get(
'/items/:id'
, itemsController.getItem);
router.post(
'/items'
, itemsController.saveItem);
return
router;
};
|
./config
当我们创建model的时候我们需要config module. config的唯一目的是检查环境类型从env文件夹加载适当的config文件. config目录只有一个文件
index.js
.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
module.exports = (
function
(env) {
var
config = {};
switch
(env) {
case
'production'
:
config = require(
'../env/production'
);
break
;
case
'development'
:
config = require(
'../env/development'
);
break
;
case
'testing'
:
config = require(
'../env/testing'
);
break
;
case
'staging'
:
config = require(
'../env/staging'
);
break
;
default
:
console.error(
'NODE_ENV environment variable not set'
);
process.exit(1);
}
return
config;
})(process.env.NODE_ENV);
|
./env
env
目录包含了对应不同环境模式的config文件: development.js
, production.js
, test.js
, and staging.js
.
Here is an example of one file:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
module.exports = {
pg: {
host:
'127.0.0.1'
,
database:
'test'
,
user:
'test'
,
password:
'test'
,
charset:
'utf8'
},
mongodb: {
url:
'mongodb://localhost:27017/test'
},
sessionSecret:
'ninja_cat'
};
|
注意了: 别在config文件中包含敏感数据, 敏感数据放到环境变量中去
./api
api
文件夹包含应用的api文件. 我用创建controller一样的方法创建api文件, 唯一不同的是controller会加载一个视图文件.
./lib
lib
文件夹在Node modules中非常普遍. 如果你的应用使用了特别的算法或helpers lib目录适合放他们. 在大多数情况下controller需要一个lib 文件来执行一些特定的任务
.
./bin
bin包含我自己的
command-line scripts. 例如:
1
2
|
#!/usr/bin/env node
console.log(
'I am an executable file'
);
|
./public
public
文件夹包含一些客户端的静态文件, 例如images, css, 前端JavaScript, fonts 等
./views
我所有的视图模板都放在这.
./test
test
目录包含了所有的测试用例.
./.gitignore
.gitignore
文件用来告诉GIT那些文件或者目录不要版本控制.
1
2
3
4
5
6
7
|
*.zip
*.psd
*~
node_modules/
bower_components/
build/
temp/
|
./.jshintrc
.jshintrc
是 jshint 的配置文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
{
"curly"
:
false
,
"eqeqeq"
:
true
,
"immed"
:
true
,
"latedef"
:
false
,
"newcap"
:
true
,
"noarg"
:
true
,
"sub"
:
true
,
"undef"
:
true
,
"boss"
:
true
,
"eqnull"
:
true
,
"node"
:
true
,
"browser"
:
true
,
"globals"
: {
"jQuery"
:
true
,
"define"
:
true
,
"requirejs"
:
true
,
"require"
:
true
,
"describe"
:
true
,
"it"
:
true
,
"beforeEach"
:
true
,
"before"
:
true
}
}
|
./package.json
package.json
是一个标准的npm文件, 列出了所有应用的 dependencies 和 metadata.
例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
{
...
"scripts"
: {
"start"
:
"node app.js"
,
"dev"
:
"nodemon app"
,
"jshint"
:
"jshint api collections config controllers env lib models public/javascripts routes test app.js"
,
"test"
:
"npm run jshint && mocha test"
,
"precommit"
:
"npm test"
,
"prepush"
:
"npm shrinkwrap && npm test"
,
"postmerge"
:
"npm install"
}
...
}
|
一些我经常用的 modules
- Express - App frameworks
- Bookshelf - Postgres 和 MySQL 的 ORM
- lodash - 工具类库
- passport - 验证
- mongoose - MongoDB ODM
- when.js - promises library
- moment - 分析, 验证, manipulating, 格式化日期