虽然用 webpack
有一阵子了,但是上次构建整个体系的时候手忙脚乱的,仅仅是找了乱七八糟的东西堆叠起来,中间的过程忘的一干二净今天去 review 自己以前写的代码完全不知道自己是怎么搞定的。这次写一个小系列把如何构建 webpack
+ redux
+ react
的体系记下来,今天是第一部分,webpack
的准备工作。
webpack
可以认为就是一个 node
版本的 make
吧,不过自然是有 js
特色的 make
了,类似的东西有很多,比如 browserify
以及 gulp
等。webpack
最终的目的就是将我们一个有众多文件的 js
的 project 变成只有一个或者多个文件的 bundles
,我在后面会结合例子做展示。并且webpack
可以支持 loader
将各种诡异的 js
方言转换成 js
比如当下比较流行的额 babel
jsx
等,所以 webpack
配合 es2015
以及 react
一起使用非常的方便。不过要说明的是对这些方言的支持是 loader
的事情,webpack
本身是只能处理原生态的 js
的。
首先自然是安装 webpack
npm install -g webpack
npm install -S webpack
然后我展示一个 webpack
的基本用法。首先看一下我们的目录结构:
├── index.html
├── index.js
├── module1.js
├── module2.js
└── package.json
其中 index.html
基本就是一个空文件
<html>
<head>
<meta charset="utf-8">
<title>index</title>
</head>
<body>
<script src="bundle.js"></script>
</body>
</html>
module1.js
module2.js
展示如下
console.log('module 1');
console.log("module 2");
index.js
通过 require
的方式引用两个模块
require("./module1");
require("./module2");
然后通过命令 webpack ./index.js bundle.js
可以将 index.js
以及其所依赖的模块打包生成一个文件 bundle.js
这样在浏览器打开 index.html
就可以看到 console
中的命令了。
在上述这么简单的情况下我们就仅仅用 webpack
的命令就可以了。不过在处理更复杂的事情的时候需要 webpack.config.js
来帮忙。这里我给出一个最小化的 webpack.config.js
的实例来替代刚才的命令。
var path = require("path");
module.exports = {
entry: [
'./index'
],
output: {
path: path.join(__dirname, "dist"),
filename: "bundle.js"
}
};
其中,entry
就是我们整个 project
的 main
。就如在前文中 webpack ./index.js bundle.js
的 ./index.js
的角色。output
则表示我们要将生成的 js
放在哪里。这里我提供了一个不同的 path
: dist
,然后依然采用 bundle.js
的名字。这样我们执行 webpack
就可以看到 dist
下出现了 bundle.js
这个文件。
那么每次改了文件之后都 webpack
是不是很麻烦,应该是有 watch
的办法吧。对的,这就是 webpack-dev-server
了。首先安装它。
npm install -g webpack-dev-server
npm install -S webpack-dev-server
执行 `webpack-dev-server --inline --hot --content-base dist/`,每次修改代码就可以自动 build 了。
既然这里我们把 content-base
设定为了 dist
那么需要把 index.html
放进去啦。最后的目录结构是这样子的:
├── dist
│ ├── bundle.js
│ └── index.html
├── index.js
├── module1.js
├── module2.js
├── package.json
└── webpack.config.js
值得一提是在 package.json
里有一个专门放置这种启动 server 的地方,就是在 scripts
下:
{
"name": "webpack-setup",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "MIT",
"dependencies": {
"webpack": "^1.13.0"
}
}
然后下次执行 npm start
就可以启动这个 dev server 啦。
DDD 即 Domain Driven Design,领域驱动设计,似乎是一个比较老的东西了,2003 年这本书就出版了。不过这么多年来我是从来都没有知晓过,直到参加了小巨人的培训。在培训的过程中,我自己尝试自己对某个领域进行建模并且以 REST API 的形式实现自己的设计,而整个过程中都要尽量的遵循 DDD 的一些设计原则,可惜培训时间太短了,我需要更多的时间去消化。半年多来我一直都在琢磨这个主题,即使工作任务很重的时候我依然在尝试把以前自己认为不遵循 DDD 设计的一些项目按照我理解的 DDD(对,我理解的,不代表是对的)进行了重写,这虽然花了不少的时间但我认为这是值得的。其中经常出现的几个名词 aggregate
value object
entity
bounded context
domain model
repository
factory
一直以来我总觉得自己没有把它们彻底的搞明白。整个体系对我来说依然没有在我的脑海中建立起来。直到最近,在认认真真的看完了几本 DDD 主题的书籍以及众多有关这个主题的文章和演讲之后,我才觉得我对这方面的理解有了起色,我逐渐的弄明白了 DDD 的真正意图,清楚了 REST 和 DDD 的关系,我才敢写下这篇文章。
培训中 REST 和 DDD 的主题是被放在一起讲解的,REST 按照 DDD 中 aggregate
的形式被组织在一起。看下面这样一组 REST API:
/users
/users/:user-id
/users/:user-id/orders
/users/:user-id/orders/:order-id
user
作为一个 aggregate
的 root
,它包含了其下所有的 order
。这样最初看起来没什么不妥,order
是随着 user
出现的,order
的生命周期是受 user
控制的。这就像是 Evans 在第六章节举的那个例子,line item
是 order
的一部分。但是在实现这部分 api 的时候我发现我有一点是难以做到的:order
是不能够随着 user
一起载入到内存的,它不像是 order
与 order line item
那样的关系。user's orders 这个集合是可以不断的扩张的,我甚至不能把一个用户所有的 order 一次性的载入到内存中,我需要分页,我需要根据默写规则对用户的订单进行筛选。此时再仔细想想 DDD 的那个例子,里面涉及了很多的并发冲突,然而当我需要拿到用户以及查询订单的时候会有什么呢?没有,什么冲突都不会发生。逐渐的我才明白其实我一开始就理解错了。事实上,DDD 关注的是一个写模型,并发的冲突都是在有写操作的时候才会出现。例子中的创建和修改订单都是写,而我所要考虑的仅仅是读而已,读事实上什么麻烦都不会产生,怎么读都不会产生问题。
再去看看 Implementing DDD 这本书的例子,application
这个 package 被分成了两部分,一部分是 Query
一部分是 Command
,没错就是 CQRS 的思路。一个个 Command
调用了 domain
中的对象和方法,实现了关键的业务。而 Query
的 Data Model 和 Domain Model 有质的区别,Query Service 甚至是直接拼装了 HQL (Hibernate SQL)。它更多的是面向 UI 的:需要展示什么就提供什么。那么这样来看,很多事情就迎刃而解了。
简单的来讲,aggregate 就是为了解决一个并发的问题。aggregate 就是定义了一个业务场景中最小的锁单元:任何人对一个 aggregate 操作时其他人都不能再对这个单元进行操作了。而整个业务中数据的一致性也是由这样的方式得到了保证。那么回到上面的例子,user
下的 order
是没有道理和 user
建立为一个 aggregate
的。user
和 order
分别是一个单 entity
的 aggregte
。
DDD 仅仅是一个写模型。在通过 REST API 暴露一个 Domain 的接口的时候要明确每一个方法分别对应了相应资源下的什么操作。虽然很多的数据和演讲中强调 REST 和 DDD 没关系,并且 Rest 的 Resource 也和 DDD 中的 Entity 没有映射关系,但是我个人觉得从 UL 的角度来说,外部所看到的东西和 Domain 所提及的东西应当一致,只是 REST 会隐藏很多细节罢了。当然,这里所谓的隐藏细节也可能隐藏的多到内外的概念是不一致的。其中 GET
和其他操作相比有着质的区别,它可能采用了纯粹的 DTO 而不涉及任何的 Domain,这样在实现模型的时候我们就不必小心翼翼的去让 Domain Model 尽量的和 Rest 所需要获取的数据保持一致了。这也是我自己在整个过程中最困惑的地方了。
到这里,按照 DDD 设计的思路就清楚多了,我不再担心如何让 GET 方法从 Domain 中拿到它想要的东西了。我可以在设计的时候首先关注更重要的业务流程,然后在需要的时候用最简单的方式提供相应的 Query Service 即可。
之前的 blog 是搭在 SAE 的 wordpress,但是由于渣浪没有回复我的账号,我默默的看着我的 SAE 云豆消耗殆尽连个充值的机会都没有,真是不知道新浪这样的状态还能撑多久。不过不管怎么样我应该还是有机会可以把我的博客拿到的,然后赶紧的把乱七八糟的博客都导过来,毕竟那个博客也用了有 5 年了吧。
之前有尝试用 jekyll 在 github 上部署 blog,那个时候 github 对 jekyll 的支持还没有那么好,我记得是不能够自己主动的部署到 xx.github.io 上的,需要自己拉一个 branch 但是现在似乎不需要了,默认部署,方便多了。而且之前的那个默认的 template 也是太丑了,自己也没有当初调整 wordpress template 的心情了哎(所以我自己对我以前 wordpress 的 template 还是挺满意的,毕竟人家默认的 template 就不错呀)。幸好找到了现在 hux 这套给力的模板,我又有动力用 jekyll 了。好多地方还是人家 hux 的名字,我也在慢慢修改了。
github 首先应该不会在短期内挂掉吧,然后人家也是给了我一个域名,唯一一点不好就是有些 2b 的学校教育网默认不能访问国外的服务器,这我就没有办法了。