# 前端篇: 數據-表現-領域
無論是MVC、MVP或者MVVP,都離不開這些基本的要素:數據、表現、領域。
### 數據
信息源于數據,我們在網站上看到的內容都應該是屬于信息的范疇。這些信息是應用從數據庫中根據業務需求查找、過濾出來的數據。
數據通常以文件的形式存儲,畢竟文件是存儲信息的基本單位。只是由于業務本身對于Create、Update、Query、Index等有不同的組合需求就引發了不同的數據存儲軟件。
如上章所說,View層直接從Model層取數據,無遺也會暴露數據的模型。作為一個前端開發人員,我們對數據的操作有三種類型:
1. 數據庫。由于Node.js在最近幾年里發展迅猛,越來越多的開發者選擇使用Node.js作為后臺語言。這與傳統的Model層并無多大不同,要么直接操作數據庫,要么間接操作數據庫。即使在NoSQL數據庫中也是如此。
1. 搜索引擎。對于以查詢為主的領域來說,搜索引擎是一個更好的選擇,而搜索引擎又不好直接向View層暴露接口。這和招聘信息一樣,都在暴露公司的技術棧。
1. RESTful。RESTful相當于是CRUD的衍生,只是傳輸介質變了。
1. LocalStorage。LocalStorage算是另外一種方式的CRUD。
說了這么多都是廢話,他們都是可以用類CRUD的方式操作。
### 數據庫
數據庫里存儲著大量的數據,在我們對系統建模的時候,也在決定系統的基礎模型。
在傳統SQL數據庫中,我們可能會依賴于ORM,也可能會自己寫SQL。在那之間,我們需要先定義Model,如下是Node.js的ORM框架Sequelize的一個示例:
~~~
var User = sequelize.define('user', {
firstName: {
type: Sequelize.STRING,
field: 'first_name' // Will result in an attribute that is firstName when user facing but first_name in the database
},
lastName: {
type: Sequelize.STRING
}
}, {
freezeTableName: true // Model tableName will be the same as the model name
});
User.sync({force: true}).then(function () {
// Table created
return User.create({
firstName: 'John',
lastName: 'Hancock'
});
});
~~~
像如MongoDB這類的數據庫,也是存在數據模型,但說的卻是嵌入子文檔。在業務量大的情況下,數據庫在考驗公司的技術能力,想想便覺得Amazon RDS挺好的。
如果是
### 表現
### 分離
### 領域
### DSL
DSL(domain-specific languages)即領域特定語言,唯一能夠確定DSL邊界的方法是考慮“一門語言的一種特定用法”和“該語言的設計者或使用者的意圖。在試圖設計一個DSL的時候,發現了一些有意思的簡單的示例。
#### jQuery 最流行的DSL
jQuery是一個Internal DSL的典型的例子。它是在一門現成語言內實現針對領域問題的描述。
~~~
$('.mydiv').addClass('flash').draggable().css('color', 'blue')
~~~
這也就是其最出名的**鏈式方法調用**。
#### Cucumber.js
Cucumber, the popular Behaviour-Driven Development tool, brought to your JavaScript stack。它是使用通用語言描述該領域的問題。
~~~
Feature: Example feature
As a user of cucumber.js
I want to have documentation on cucumber
So that I can concentrate on building awesome applications
Scenario: Reading documentation
Given I am on the Cucumber.js GitHub repository
When I go to the README file
Then I should see "Usage" as the page title
~~~
#### CoffeeScript
發明一門全新的語言描述該領域的問題。
~~~
math =
root: Math.sqrt
square: square
cube: (x) -> x * square x
~~~
#### JavaScript DSL 示例
所以由上面的結論我們可以知道的是,難度等級應該是
內部DSL < 外部DSL < 語言工作臺(這是怎么翻譯的)
接著在網上找到了一個高級一點的內部DSL示例,如果我們要做jQuery式的鏈式方法調用也是簡單的,但是似乎沒有足夠的理由去說服其他人。
原文在: [http://alexyoung.org/2009/10/22/javascript-dsl/](http://alexyoung.org/2009/10/22/javascript-dsl/),相當于是一個微測試框架。
~~~
var DSLRunner = {
run: function(methods) {
this.ingredients = [];
this.methods = methods;
this.executeAndRemove('first');
for (var key in this.methods) {
if (key !== 'last' && key.match(/^bake/)) {
this.executeAndRemove(key);
}
}
this.executeAndRemove('last');
},
addIngredient: function(ingredient) {
this.ingredients.push(ingredient);
},
executeAndRemove: function(methodName) {
var output = this.methods[methodName]();
delete(this.methods[methodName]);
return output;
}
};
DSLRunner.run({
first: function() {
console.log("I happen first");
},
bakeCake: function() {
console.log("Commencing cake baking");
},
bakeBread: function() {
console.log("Baking bread");
},
last: function() {
console.log("last");
}
});
~~~
這個想法,看上去就是定義了一些map,然后執行。
接著,又看到了一個有意思的DSL,作者是在解決表單驗證的問題[《JavaScript DSL Because I’m Tired of Writing If.. If…If…》](http://byatool.com/ui/javascript-dsl-because-im-tired-of-writing-if-if-if/):
~~~
var rules =
['Username',
['is not empty', 'Username is required.'],
['is not longer than', 7, 'Username is too long.']],
['Name',
['is not empty', 'Name is required.']],
['Password',
['length is between', 4, 6, 'Password is not acceptable.']]];
~~~
有一個map對應了上面的方法
~~~
var methods = [
['is not empty', isNotEmpty],
['is not longer than', isNotLongerThan],
['length is between', isBetween]];
~~~
原文只給了一部分代碼
~~~
var methodPair = find(methods, function(method) {
return car(method) === car(innerRule);
});
var methodToUse = peek(methodPair);
return function(obj) {
var error = peek(innerRule); //error is the last index
var values = sink(cdr(innerRule)); //get everything but the error
return methodToUse(obj, propertyName, error, values); //construct the validation call
};
~~~