2017-10-30

Salesforce: Objects Generalization and Specialization

Salesforce 在設定畫面上的 object 名詞可能是個讓習於物件導向程式開發者困惑的地方:畫面上的 object 代表 type definition,而非 instance,真正描述 instance 的名詞是 record。

然而在處理 generalization & specialization 議題時,畫面上又給了一個新名詞,record type。系統管理者可以把所有可能用到的欄位都開在同一個 object 裡,透過 record type 的方式,限定各個 record type 所能看到的欄位。值得注意的是:這邊的縮限手段,是採用綁定 layout 的方式達成,如果使用 Apex 程式存取,則不在此限。

從 generalization & specialization 的抽象層次來看,record type 確實提供了一種 specialization 的途徑,與物件導向中的 subclass 類似,但很不一樣的是:record type 處理的是資料面的 specialization,讓特定 record type 只能看到原來眾多欄位的子集合,而 subclass 同時兼顧資料面與行為面的 specialization,各個 subclasses 能夠自己額外定義欄位並實作不同行為。

此外,record type 並不像物件導向的繼承關係將共同的部分獨立抽取至 super class,相對於 record types,object 等於是所有欄位的大雜燴,看不到哪些是共同的欄位,對於 generalization 有潔癖的人來說,似乎只能用類似物件導向 composition 的方式處理 [1],也就是 Salesforce 中的 master-detail object relationships [2]

使用 master-detail relationships 實現 generalization 難免有強加物件導向觀念的味道,但更不幸的是:即使採用這麼複雜的設定,仍然無法達成目的。原因在於:使用 master-detail relationships 實作 generalization 時,生命週期控制在 master 端,相當於是 specialized subclass;但在數量對應上 master 端屬於 one-side 而非 many-side,違背 generalization 的初衷。

see also:
[1] Salesforce Developers Forum: Custom Objects and Inheritance
[2] Salesforce Object Relationships

2017-10-24

Apex Serialize Objects to XML

先前的文章 [1] 中提到,Salesforce 的 REST 同時支援兩種輸出格式,XML 與 JSON;client 可以透過 HttpRequst header 中 Accept 屬性指定想要的 content type;這兩個 serialization 的過程對於 REST service 的實作者而言是完全 transparent 的,意即實作時 method 並不 return string 型別,而是直接 return sObject,由系統的 outbound messaging 機制直接處理掉。

然而對於 Apex 開發者而言,程式語言級別對 XML 與 JSON 在 serialization 的支援度就差很遠。以 JSON 為例,無論物件多複雜,要轉成 JSON string 只要一行 JSON.serialize() 就可以搞定 [2],但是轉成 XML string 時,無論使用 XmlStreamWriter [3] 或 DOM.XmlNode [4] [5] 都需要針對物件中各個 fields 作繁瑣地處理。

為了達到快速生成 XML 字串,我們可以透過 (1) 實作一 REST service;(2) 在 Apex 中呼叫該 service,並將 HttpRequest Accept header 設定為 'application/xml' 的方式,快速從 HttpResponse.getBody() 取得 outbound messaging 生成好的 XML 字串。

概念上是走 loopback invocation 的方式,利用既有的機制,寫最少的程式,完成任務。

see also:
[1] Salesforce RESTful Services: Response Content-Type
[2] JSON Support: Roundtrip Serialization and Deserialization
[3] XML Support: Writing XML Using Streams
[4] XML Support: Reading and Writing XML Using the DOM
[5] XmlNode Class
[6] Salesforce Trailblazer Community Idea: Convert Object to XML (Serialize)

.NET: Framework or Virtual Machine

一般提到 .NET 多半使用 .NET framewrok 這個名詞,內容可能包括:
  • Programming Languages: 包括 C#, C++/CLI, F#, IronLisp, IronPython, VB.NET, Powershell 等 [1]
  • Compiler
  • CLI Standard Library: 裡頭主要有 Framework Class Library (FCL) [2] 與 Base Class Library (BCL) [3]
  • Common Intermediate Language (CIL)
  • Common Language Runtime (CLR)
對於傳統使用 C++ 搭配 MFC 開發視窗程式 (GUI desktop application) 的開發者而言,視線會集中在 C# 與 CLI Standard Library;此時 .NET 更趨近於 framework 的角色,提供眾多的 classes,開發人員根據 framework 規範撰寫 component。

然而就 .NET 整體架構來看,最大的改變在於提供了統一的 intermediate language (IL) 介面,使得前端可以同時支援多種程式語言,呼叫同一套 standard library;且這個 IL 設計是獨立於 CPU architecture 與 operating system platform 之外的 [4]。換言之,開發者可以自由地在任何想要的平台上開發 IL 所需的 runtime,因此角色上更接近 virtual machine。

雖然說大多數的 .NET 應用程式都在 Windows 與其內建的 runtime,但還是有不少開發者作過這方面的努力:
但實際使用上,卻不那麼順利。主因在於 .NET 雖然 CIL 提供了優良的隔離,獨立於 Windows 之外,但最重要的 CLI Standard Library 卻因為服務視窗程式開發者的初衷,納入了太多 Windows 特有的 library,尤其是圖形介面 (GDI) 這段,導致許多在 Windows 上開發的元件,無法順利過渡到其它 runtime 執行。

換言之,雖然 CIL 與 CLR 提供了很大 virtual machine 的特點,但身為 runtime infrastructure 重點的 standard library 並未提供相應的設計,導致整個 .NET 回到近乎 Windows 獨有的情況。

這幾年微軟開始改革 Windows Server 的產品線 [8],將圖形介面剝離出來,打破原本 Windows runtime 包山包海的狀況,同時也開始推更乾淨的 .NET Core;就架構而言這應該有助於第三方 .NET runtime 的普及,但微軟將發展 Mono 的 Xamarin 收入麾下卻讓非微軟勢力瞬間消失。

注意:Mono 在 runtime 實作仍是按照舊有的 CLI (ECMA-335) [9] 而非 CoreCLI,因此 Mono 是否會如 .NET Core 壯大仍有很大變數。

此外,Mono 的貢獻在不僅僅於 runtime,還有跨桌面平台的解決方案,這塊的未來應該屬於 Electron [10];至於依賴 Mono runtime 的跨行動平台方案 - Xamarin,個人認為疑慮就很大,目前 Windows Phone 已確定式微,微軟還願意花多少資源給跨行動平台開發方案,看來非常不樂觀。

這兩年微軟也開始引入 LLVM 技術,實作相關的 compiler (LLILC),想要深入這個主題的可以一起參考看看。

see also:
[1] Wikipedia: CLI Languages
[2] Wikipedia: Framework Class Library (FCL)
[3] Wikipedia: Standard Libraries (CLI) 
[4] Wikipedia: Common Intermediate Language (CIL)
[5] Wikipedia: DotGNU
[6] Wikipedia: Protable.NET
[7] Mono Project
[8] Wikipedia: Microsoft Windows Server Core
[9] ECMA-335: Common Language Infrastructure (CLI)
[10] Electron

2017-10-13

SSL Certificate Standards

要搞懂系統認證之前,首先要對非對稱式加密有基礎瞭解,不外乎就 2 種 key:
  • Public key: 用於加密與驗證簽章
  • Private key: 用於解密與產生簽章
至於這兩把 key 的產生通常會由共同信任的第三方,也就是 CA (Certificate Authority) 發出。[1]

Standards: Encoding Rules & Formats

接下來可能會遇到各種檔案,這裡頭牽涉的標準主要有 2 類:
  1. Encoding rule: 說明內容要如何編碼,主要有 DER 與 PEM [2] 兩種,前者的輸出為 binary,後者為 base64。
  2. Message or file format: 定義檔案應該包括那些資訊,主要有 PFX, P12 (PKCS #12), CSR (PKCS #10), X.509
  • PFX 或 P12 (PKCS #12) [3]: 也就是 CA-signed certificate;這類檔案帶有 private key,一般會被保護起來,很少複製傳遞。
  • CSR (PKCS #10) [4]: Certificate Signing Request; 為 client 向 CA 要求簽發憑證時使用,帶自身站台資訊,最少使用。
  • X.509 [6] 或 P7C (PKCS #7) [7]: 最常見,用於存放 public certificate 或 signature,其中 P7C 預定為 BER encoding,因此不會與其它 encoding rule 併用。
File Name Extensions

上頭這些標準名稱都常見於副檔名,譬如說:
  •  *.pfx 與 *.p12 一定是 CA-signed certificate,未指定 encoding rule,但通常為 binary,可與 PEM 合併使用。
  •  *.p7c 一定是 public certificate,固定為 BER encoding。
  •  *.der 與 *.pem 通常僅為 public certificate,用副檔名描述編碼方式。
除此,*.cer 與 *.crt 也是常見的副檔名,大多也僅為 public certificate,可套用任何 encoding rule。

最後是這些 key 如何被保存到 client 與 server;對系統來說,存放位置分為 TrustStore 或 KeyStore,分別存放扮演 consumer 與 provider 時,使用的 public certificate 與 private keys。
  • 與一般 symmetric authentication 所稱的 certificate 一詞不同,symmetric authentication 中的 certificate 多半為 username/password,但這邊的 certificate 泛指 signature 或包含 private key。
  • 例如:CA-signed certificate 即為兩者組合,而 public certificate 則為 signature。所以當單純看到副檔名 *.crt 或 *.cer,並無法確定內容是否包括 private key,類似的情況也發生在 *.pem 的檔案。

ps. Java 的 KeyStore 副檔名通常為 *.jks,由於 KeyStore 用來存放 CA-signed certificate 的關係,有些系統便使用 Java KeyStore 檔取代 *.pfx 或 *.p12,作為匯入 CA-signed certificate 時使用。


see also:
[1] Public Key Infrastructure
[2] PEM (Privacy-enhanced Electronic Mail): Base64 encoded DER certificate
[3] PKCS #12: Personal Information Exchange Syntax Standard
[4] CSR (Certificate Signing Request)
[5] PKCS (Public Key Cryptography Standards)
[6] X.509
[7] PKCS #7: Cryptographic Message Syntax
[8] DER vs. CRT vs. CER vs. PEM Certificates and How To Convert Them


Salesforce RESTful Services: Publish

當使用 Salesforce 作為 API provider 時,預設採用 OAuth 2.0 作為認證機制,如果想簡化認證過程,則可透過將 API 發佈到公開站台的方式完成,此時需要針對 site profile 進行設定。
  • 首先要進到 site details page;在 Setup / Build / Develop / Sites,點選 Site Label 欄位 (不是 Edit 也不是 Site URL)。
  • Site Details page 上方有個 Public Access Settings button,點進去後可能會因為 Org 不同而出現不同畫面:
  • Personal Develop Org: 畫面為一個很長的表格,要設定的地方有:Enabled Apex Class Access, Field-Level Security 與 Object Permissions。
  • Production Sandbox Org: 分成 Apps 與 System 兩段,需設定的連結落在 Apps 裡頭,分別是 Apex Class Access 與 Object Settings。
設定的重點不外乎:service class 本身以及相關需要存取的物件欄位,因此只要分別在 Apex Class 與 Objects 中將相關權限打開即可。

see also:
[1] Public RESTful Web Services on Force.com Sites
[2] Public Access Settings for Force.com Sites
[3] Public Access Settings in Salesforce

Salesforce RESTful Services: Response Content-Type

Salesforce 實作 RESTful 時,不需額外的程式碼,系統會自動支援 2 種 response Content-Type:JSON 與 XML。

但由於回傳 XML 時,內部的 tag 名稱會代出自訂欄位與物件的名稱,並不存在於 W3C 預設 namespace 中,而 Salesforce 亦不會代為產生對應的 XML schema,因此對於某些 XML parser 來說會造成 parsing error,比較直覺的解法是繞過回傳 XML 改使用 JSON。

ps. 實際測試時,回傳的 XML 字串會設定 namespace 為 http://soap.sforce.com/schemas/class/ 開頭的 URL,但並未實際生成 XML schema。

前面說過 Salesforce 會自動支援 2 種 response Content-Type,切換的依據是根據 request header 中的 Accept 屬性。
  • 一般瀏覽器 (Chrome, Firefox, IE) 預設的 Accept Content-Type 為 text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8,此時 Salesforce 會優先取用 application/xml
  • 當未設定 Accept Content-Type,或指定 Accept Content-Type 為 application/json 或空白時,Salesforce 預設會以 JSON 作為 response Content-Type。
如果想直接在 server 端指定只支援 JSON,則必須將 method 的 return type 改為 void,並自行指定 HttpResponse,填入 JSON 字串。

RestContext.response.addHeader('Content-Type', 'application/json');
RestContext.response.responseBody = Blob.valueOf('{ "result": true}');

ps. 回傳的 charset 預設皆為 UTF-8,不需要額外設定 Accept-Charset。

see also:
[1] Apex REST Methods
[2] How can I return JSON object from REST service?

2017-10-06

Apex Describe Information

Salesforce 針對 SObject 與 field 分別提供相關的 describe information (a.k.a. metadata),有點類似 Java 中的 java.lang.reflect.Type 與 java.lang.reflect.Field;不過 Salesforce 進一部拆分成 token 與 describe result,前者為一個 compile-time generated & runtime immutable reference,沒有細部資料,但可用於比對 (==),而後者才是真正具備細部資料的結構。

  • Token 轉換至 describe result 的行為稱之為 describe,呼叫 getDescribe() method,可視為 dereference & data loading。
  • SObject 與 field 的 token 與 describe result 資料型別與相關取用方式分別為:
下圖為 4 種 describe information 的產生與轉換方式:
  • 除了 SObject 的 token 與 describe result 有機會從 instance 取得,其餘都必須在 Apex code 中指定 SObject 或 Field name 才能獲得。
  • 不只 SObject instance 有提供 getSObjectType() method,其它提供此 method 的還包括:SObject Describe Result, List 與 Map;其中 List 與 Map 呼叫 getSObjectType() 時等同於針對其 element instance 呼叫該 method。

see also:
[1] Apex Developer Guide: Schema Class
[2] Apex Developer Guide: Schame Namespace
[3] Apex Developer Guide: Dynamic Apex

2017-09-28

Salesforce Apex REST

REST 是目前實作 web API 時較為普遍的選擇,Salesforce 原生在 APEX 中也提供了這方面的支援,方便開發者自訂 API 作為系統整合使用。在 APEX 有以下 6 個 REST annotations:
  1. @RestResource
  2. @HttpDelete
  3. @HttpGet
  4. @HttpPatch
  5. @HttpPost
  6. @HttpPut
值得注意的是:APEX REST annotations 中只有 RestResource 才有 urlMapping 參數,且該 annotation 為 class level;換言之:
  • 每一個 service implementation class,針對各種 HTTP requests 類型只能有 1 個 method;不能同時宣告多個 @HttpGet methods 在 1 class 中;
  • Service implementation class 中的 class name 並不會浮現在 URL 中。
see also:
[1] Apex REST Annotations
[2] Creating REST APIs using Apex REST

Kayako Template & Language Engine

Kayako Fusion 是一套 on-premise helpdesk 系統,使用的語言為 PHP,framework 為自有的 SWIFT framework,在修改或整合上會需要一些功夫去瞭解系統架構,才不至於出現難以維護或資料不一致的現象。最近處理了一段有關多國語系的問題,這邊順便將心得記錄一下。

首先,Kayako 中提供了兩個元件來處理多國語系:Template Engine 與 Language Engine,分別負責處理文本的模版 (template) 跟各語言詞彙 (phrase),資料來源都同時支援 database 與 file system, 下圖說明這兩個元件的關係與 data flow。


值得注意的是:
  • Language Engine 透過一個 key-value array 記錄某個 phrase key 對應的字串,其中 phrase key 並不包括語言別資訊,因此一個 engine instance 只能處理一種語言,否則會出現 key conflicts 現象。
  • Template Engine 不涉及語言別的問題,因此系統可共用一個 template engine instance;反之 Language Engine 必須針對需要的產出的語言,生成對應的 instance。
在 Kayako 原本的邏輯中
  • 取用 Language Engine instance 的邏輯被放在 TemplateEngine->Get() 中,除了 Web 操作根據 cookie 在底層 SWIFT 生成不同語言的 instances 外,其餘都使用預設的 (English) Language Engine instance。
  • TemplateEngine->Get() 不帶 phrase keys 參數,Language Engine 負責載入所有的 phrases,交由 Dwoo core 產出最終文本。

因此如果我們要讓非 Web 操作也能產出多語言內容的話,有兩種選擇:
  1. 修改  TemplateEngine->Get(),新增語言別參數,動態產出對應的  Language Engine instance。
  2. 在呼叫  TemplateEngine->Get() 前預先產出所需的 phrases,並於呼叫時傳入。
這邊我採用第 2 種作法,目的在於降低 Language Engine 載入的 phrases 數量。

假設今天我要製作一個具備多國語系的畫面,除了預先將 template 跟 phrase 準備好存入 database 或 file system 外,主要的 rendering 邏輯如下:
  • 根據需要的語言別,初始化 Language Engine instance。
  • 根據 phrase keys 與 language id (or language code) 透過 Language Engine 載入 phrases。
  • 呼叫 Template Engine 中的 Get(),傳入 template key 與 phrases,生成 output content;其中 template engine 會在 SWIFT_Base 中初始化,如果已繼承 SWIFT MVC classes 或 SWIFT_Library,這邊可以直接取用,不需要另行初始化。

2017-09-21

Salesforce: More about SOQL

上一篇整理了 Salesforce 中各式 object relationships 與相關 SOQL patterns;這邊想多聊一些 SOQL 撰寫時要注意的小地方。

Lazy Fetch

SOQL 算是把 lazy fetch 發揮得淋漓盡致;首先,它不像 SQL,SOQL 中並不提供 SELECT * 的語句,所有需要的 fields 除了 ID 都必須完整列舉。其次,即使只 SELECT 一個 field,回傳型別仍為 List<T> where T extends sObject,以下面這個針對 Account 物件搜尋的 SOQL 為例:
SELECT Name FROM Account
回傳的型別是 List<Account> 而不是 List<String>。

Query from One-side
如同操作 relational database,Salesforce 也提供了一些介面方便使用 SOQL 查詢系統中的資料,無論是 Workbench 或 Eclipse 中的 schema view,都會將結果以表格 (tabular) 的方式呈現。這跟 SOQL 以 object-oriented 的操作思維有些衝突,尤其是針對 one-to-many relationships 查詢時。以下是從 one-side 出發使用 outer JOIN 後在 Workbench 中得到的錯誤訊息。
看來 Salesforce 在這塊並不像 report 介面提供表格中再展開子表的能力,直接給了 parent relationships queries are disabled 的錯誤訊息。

2017-09-19

Salesforce Object Relationships

Salesforce 中有關 objects relationships 對新手往往是比較難懂的,主要原因包括:
  1. Object modeling 時採用的 schema builder 看似 ER model,但實際使用 SOQL 操作時,卻是以物件導向的觀點。
  2. SOQL 沒有 JOIN,只能透過 relationship traversal with path expression 完成各式的 implicit JOIN。
  3. Object 中記錄 relationship 的 field naming,有獨特的 convention,有時使用 __c,有時卻要使用 __r。
  4. Relationship 的 cardinality 固定為 one-to-many;使用 SOQL traverse relationship 時,從 one side 或 many side 出發會需要截然不同的 SOQL statement patterns。
另外還有幾點對撰寫 SOQL 關係不大,但也容易造成困擾:
  1. 設定 relationships 可能分為兩類:lookup 與 master-detail (a.k.a. MD)。
  2. 以物件導向的角度,上述分別是 aggregation 與 composition;前者是 whole-part 的關係,後者有一致的生命週期。
  3. 文件中的 parent 與 child,並非專屬於 master-detail relationship,純粹分別用於 relationship 中的 one side 與 many side。
[1] 是目前看過的參考資料中,最為完整的一篇,建議可以從這篇開始入門;相對其他文件,此處的名詞定義叫為明確,範例也相對清楚。這邊特別說明了如果 SOQL 以 one side (parent) 作為起點時,需使用 nested SELECT:
  • Both relationships are defined from the many-to-1 side, namely from a child to a parent. Note that we have utilized a NESTED select to obtain the records of related children from the parent. This kind of expression is very powerful in obtaining related records traversing from the 1 side in a 1-m relationship.
  • It is a useful pattern to obtain related information on a parent and all its children via traversing path expressions with relationship fields.
另外,當 SOQL 以 one side 出發並使用 nested SELECT 時,回傳結果的數量並非 Cartesian product,而是以 nested object 的方式呈現,意即每一筆 parent object 中含有多筆 child objects。

下表整理了 SOQL 針對不同 side 作為起點,如何透過 relationship traversal 達到各式 JOIN 的 patterns:
Outermost FROM Clause
One Side Many Side
Inner JOIN 在 WHERE clause 中使用 nested SELECT
SELECT Name
FROM Position_c
WHERE Id IN (SELECT Position__c FROM Job_Application__c)
在 WHERE clause 中直接取用 relationship
SELECT Name,Position__r.Name,
FROM Job_Application__c
WHERE Position__r.Department__c = ‘Sales’
Outer JOIN 在 SELECT clause 中使用 nested SELECT
SELECT Name, (SELECT Name FROM Job_Applications__r)
FROM Position__c
在 SELECT clause 中直接取用 relationship
SELECT Name, Position__r.Department__c
FROM Job_Application__c
ps. 上述 SOQL 中,只有在取用 relevant object 的欄位時才需要使用 __r notation,若只需該物件 id,則用 __c 即可。

[2] 說明了定義 relationship 時的命名原則:
  • 在 parent object 中定義 relationship 時:名稱須 unique to the parent,並且使用複數形的 child object name 命名。
  • 在 child object 中定義 relationship 時:名稱須 unique to the child,並且使用單數形的 parent object name 命名。
[3] 提供了一個從 child object (Daughter) 定義 lookup relationship 的範例
  • 按前述第 (2) 項原則,relationship filed 的命名應使用單數的 parent object name,此處命名為 Mother_of_Child (應可簡化為 Mother)。
  • Child Relationship Name 則以 child object 為主,命名為 Daughters。
    => Lookup relationship 中才談 Child Relationship Name,master-detail relationship 無此欄位。
  • 從 child object 為起點實作 outer JOIN:SELECT Id, FirstName__c, Mother_of_Child__r.FirstName__c FROM Daughter__c WHERE Mother_of_Child__r.LastName__c LIKE 'C%'
  • 從 parent object 為起點實作 outer JOIN:SELECT LastName__c, (SELECT LastName__c FROM Daughters__r) FROM Mother__c
see also:
[1] A Deeper Look at SOQL and Relationship Queries on Force.com
[2] Understanding Relationship Names
[3] Understanding Relationship Names, Custom Objects, and Custom Fields

2017-07-20

MVC, ASP.NET, and Salesforce VisualPage

表面上寫了好一陣 web application,骨子裡更多是寫 backend, libraries 與一些整合性的 service,UI 這塊一直非常薄弱。一開始想學 Struts2,看了些書,但一直沒落實到開發上;後來因為 Activiti BPM 接觸到 Vaadin 6,大概知道 component-based MVC 的樣貌,之後又碰了些 ASP.NET WebForm 這種以 view 為主的架構,算是有些粗略印象,該是要好好釐清的時候。

傳統的 MVC 源自單機的圖形化程式,model, view, controller 都在同一個 runtime 上,彼此溝通無礙;model 變化時可以順利更新 view,controller 只要負責處理 request 與 response 就好,簡單來說就是源自 UI control 與 model 的 events。其中 model 更新 view 這段連結又稱為 data binding。

這件事到了 web application 世界中有了大挑戰。

在 web application 中,view 屬於 client code,由 rendering engine 負責生成,而 controller 與 model 為 server code,server 端無法隨時連接 client 作畫面更新;因此出現了 MVC Model2。MVC Model2 並沒有解決問題,只是因應限制,釐清與傳統 MVC 的不同,MVC Model2 中,model 不負責將結果更新到 view,改由 controller 負責,至於如何實現一時還沒有答案。

這個時期出現幾種作法,最直覺得就是在 view 上頭掛一段 JavaScript,定期刷新頁面,包括畫面跟資料。微軟為了比較好的使用者體驗,在 1999 率先提出 XMLHttpRequest (XHR) 的概念,也就是說 client 刷新不再以整個 view 為單位,可以只針對需要的資料取得相關的 XML 即可。整個標準化的過程一直到 2002/6 Mozilla Gecko 發佈 1.0 版、2004/2 Safari 1.2 發佈之後才真正落實。

2004-2005 這兩年迎來巨變。

Gmail 跟 Google Maps 相繼於 2004/4 與 2005/2 發表,大家也發現 XMLHttpRequest 可以搭配 client-side JavaScript asynchronous 的行為,讓 JavaScript 負責處理 XMLHttpResponse 就好,不但可以刷新部份頁面,也可以部在一開始就載入完整頁面,當使用者觸發後再進一步抓資料,且不影響既有畫面瀏覽。AJAX 正式登場,並作為 2004/11 Firefox 1.0 發表時的主打特點。

某程度上來說 AJAX 算是解決了部份 data binding 的問題,但主動權仍落在 client 端,並不是真的由 controller 更新 view,屬於單向 (one-way) data binding。即便如此,AJAX 也讓更多人把目光從 data binding 轉移到另一個重點,model 的複用,衍生 component-based MVC frameworks。

對於初入前端的人來說,最棘手的就是各瀏覽器相容性問題,一種解法是使用 cross-browser JavaScript UI libraries,另一種就是交由 MVC frameworks 產出 cross-browser UI components。Component-based MVC 就是後者的產物。

這邊的 component 指的是 UI component;例如一個下拉選單,或 textbox;通常會帶一些資料跟檢核機制。相對於 MVC,component 比較像是 model 在 view 中的呈現,卻又帶一點 controller 對資料檢核的邏輯,但其呈現的不是一整的 page 而是各別 model。使用 component-based MVC 有個很大的好處,前面說的 one-way data binding 都在 framework 中處理好了,不需要額外程式碼,開發人員只要專注如何組裝、複用這些 UI components 即可。

但也因為 components 大大削弱了原本 MVC Model2 中 controller 的角色,處理 request 與更新 view;因此這類 component-based MVC framework 標榜的 architectural pattern 往往不是 MVC Model2,而是 MVP 或 MVVM 等。

ASP.NET 中的 code behind 算不算是 controller?
先回頭看看 controller 的 3 項主要職責:(1) 處理 request;(2) 根據執行結果選擇不同的 view;(3) 更新 view 中 model 的值。從功能性來看,code behind 完全能達到上面三者的要求,那究竟怪在哪裡?

首先 code behind class 裡頭的 member fields 多半宣告在 ASPX/ASPC 中,而 ASPX/ASPC 又只能綁定一個 code behind class,導致兩者 1:1 的強耦合關係,微軟更因此給了個很妙的名稱,page controller;這樣的模糊命名讓原本寫 ASP.NET Web Forms 的開發人員,瞬間也可以宣稱自己在寫 MVC;只是沒有以 ASP.NET MVC 為基礎,真正的 ASP.NET MVC 用的是 front controller pattern。

除了耦合性議題,另一個 page controller 讓人覺得詭異的地方是:在針對執行結果導向對應的 view 時,page controller 往往使用 HttpRequest.Redirect,依賴瀏覽器處理 HTTP redirect 行為,而不是走 server-side internal routing。

所以這個問題若使用微軟模糊的回答,答案是 YES;但面對多數的 MVC framework 是完全對應不起來。

Salesforce VisualPage 的 controller
相對於 ASP.NET ASPX/ASPC 與 code behind 的結構,VisualPage 也有個類似 code behind 的 class,稱為 controller。比 code behind 稍微好一點,VisualPage 所有欄位的宣告被放在 controller,換言之,controller 可脫離 VisualPage 獨立測試。此外在預載欄位資料上,ASP.NET 透過 code behind 塞入資料,而 VisualPage 的 controller 只提供 getter,把塞資料的主動權交給 view 本身,跟 AJAX data binding 的作法一致。

至於連動選單的更新,VisualPage 的作法就完全按照 AJAX data binding,不需要重載整個頁面,由瀏覽器送出 XMLHttpRequest 後刷新 UI component 完成。

See also:
[1] XMLHttpRequest (wikipedia; Mozilla.org)
[2] AJAX Programming (wikipedia)
[3] Ajax: A New Approach to Web Applications
[4] GUI Architectures (Martin Fowler)
[5] MVC是一個巨大誤會
[6] MVVM (wikipedia)
[7] MVP (wikipedia)
[8] Webforms vs MVC and Why MVC is better?
[9] MVC-structured Visualforce Example

2017-05-02

Visual Studio Solution File and IIS Express Settings

使用 Visual Studio 開發 Web application 時,solution file (*.sln) 是主要的設定檔。Visual Studio 這邊一個設計不好的地方在於,sln 檔裡頭會記錄 IIS Express 使用的 port number。

傳統上一般開發好的 Web application 會透過手動複製或 web deploy 的方式佈署到執行環境上,但在開發測試時,實際佈署的執行環境是 Visual Studio 自帶的 IIS Express;如同一般的 IIS 一樣,IIS Express 也需要一個 port number 來作為 HTTP entry point,例如:http://localhost:54321。

應用程式跟 port number 的對應關係會記錄在 %USERPROFILE%\Documents\IISExpress\config\applicationhost.config 中,主要記錄 site name, physical path, and port number;一般當多人協同開發時,各自的 physical path 皆不相同,不會將 applicationhost.config commit 上 version control,而是交給開發人員各自管理。

麻煩的是帶有 port number 作為開發專案的重要設定必須 commit 到 version control,加上該設定又帶了 port number,間接限定了所有共同開發的人員對於相同的 solution 都必須手動維護 IIS Express applicationhost.config 設定,使用同一個 port number。

ps. Windows 預帶一些指向特定目錄的環境變數,這些變數在設定應用程式時經常用到,但卻在 系統內容/環境變數 中找不到,它們的正式名稱是 Constant Special item ID List (CSIDL),這邊的 %USERPROFILE% 就是常見的一個。

see also: CSIDL

2017-04-20

Headset 3.5mm Conncector Standards

現在的人因為智慧型手機汰換很快,手邊越來越多附贈的耳機跟充電線,這些耳機多半具備音量線控、來電接聽、跟麥克風的功能,跟傳統立體聲耳機不太一樣,雖然說很早就遇過插進一般電腦耳機孔左右聲道會有些問題,但也一直沒細究。查了一下才發現這裡頭還有不一樣的標準,雖然都是 3.5mm 接頭,卻又互不相容。

TRS 接頭
TRS 的全名是 tip-ring-sleeve [1],也就是傳統常見的立體聲耳機接頭,金屬接點分成三段,分別是尖端 (tip)、基部柱狀 (sleeve)、以及中段 (ring)。Tip 跟 ring 這兩個名稱源自古早手動接線的電話系統 [2],當時並沒有左右聲道,但後來用在耳機接頭時,分別作為左右聲道訊號使用;而 sleeve 則用於接地。至於更古早的單音耳機,因為只需要兩段金屬接點,因此使用的接頭是 TS 接頭。

目前常見智慧型手機附贈的耳機,接點分成四段,稱為 TRRS 接頭。就訊號來說,尖端兩個與 TRS 相同,靠基部這邊的 RS 則又分成 CTIA (又稱為 American Headset Jack; AHJ) [3] 跟 OMTP [4] 兩種標準,前者的 RS 分別為接地與麥克風,後者則剛好顛倒過來。因為聲音輸出時,訊號必須對應接地才能成功推動發聲元件,因此 CTIA 與 OMTP 間混用會因為找不到接地線,而無法正確發聲。

這兩個標準大致上老舊跟山寨的產品多使用 OMTP,像是 SONY ERICSSON 跟 Coolpad/OPPO/ZTE;而 CITA 則廣泛用在現在市面上多數手機,包括 Apple/ASUS/LG/Meizu/SONY/Xiaomi;至於 HTC/MOTO/NOKIA/SAMSUNG 則要看機型而定。

如果需要在電腦上使用手機附贈的耳機,可以購買轉接線完成。

See also:
[1] Phone Connector (Audio)
[2] Tip and Ring
[3] Cellular Telephone Industries Association (CITA)
[4] Open Mobile Terminal Platform (OMTP)

2017-04-11

Customer Relationship Management (CRM)

一般來說 CRM 系統會包括 3 個主要功能模組:行銷 (marketing)、業務 (sales)、客服 (customer support);可以直接對應到公司面對客戶時的主要三個階段。其中 CRM 的業務模組通常只提供報價 (quote) 功能,至於成交後的訂單 (order)、合約 (contract)、倉儲 (inventory)、發票 (invoice) 等管理功能,多半會回到 ERP 的模組中,而不在涵蓋範圍中。

這 3 個模組中,又以業務功能為 CRM 的最核心,CRM 的行銷功能往往專注於 target list、bugget planning,輔以更專業的系統負責 custom portal、multi- campaign channel management (newlettter, email, phone call, social networks)、閱讀追蹤、SEO 等功能;而客服系統也往往會拆分出 helpdesk 系統,提供更多案件 (case) 追蹤功能。
MarketingSalesCustomer Support
Accounts
Contacts
Targets
Campaigns
Leads
Opportunities
Quotes
Cases

上表是 CRM 中常見名詞跟 3 個功能模組的關聯對應,其中 campaigns, opportunities, cases 是分屬 3 個功能模組的活動紀錄,其他則為客戶資料。Accounts 跟 contacts 是相對中性的;targets 是蒐集到客戶資料後的第一階段,經 marketing/sales 資料審核後轉成 leads,視連繫狀況再轉為 prospects,等到報價確定成交成為訂單後,再轉為 customers,由客服模組負責。[1][2]

客戶數在各銷售階段的轉換過程中會逐漸縮小,呈漏斗形,故稱為 sales funnel。一般來說 sales funnel 以 leads 為起點 (更之前的 anonymous 資料未存於系統中),後續才是 prospects 跟 customers;在 Salesforce 跟 SuiteCRM 的設計中,lead 可轉換為 account,prospect 跟 customer 都被視為是一種 account type [3][4]。

Salesforce 預設的 account type 有 Analyst, Press, Competitor, Prospect, Customer, Reseller, Integrator, Investor, Partner, and Other [4].

部分專營 B2B CRM 的廠商會在 CRM 前再多一個 Marketing Automation Systems (MAS),負責蒐集潛在客戶資料,經過評分過濾成為 lead [5]。

see also:
[1] Customer Relationship Management: Aspects, Strategy and Use of Technology
[2] What is the Difference between Campaign, Opportunity, and Case?
[3] SuiteCRM User Guide
[4] What are the default picklist values in Salesforce?
[5] B2B Marketing Stack Basics: CRM, MAS, and The B2B Lead Lifecycle
[6] The True State of Open Source CRM
[7] 行銷與業務有何不同?這45件事讓你搞清楚


2017-04-07

Contract, Interface, and API Document

有一些程式經驗的人大多聽過 design by contract 這個詞,意思大致是按照 contract 實作;部份習慣於 object-oriented programming 的人很自然的就把 contract 誤以為等價於 interface,中了 interface-as-contract myth [1]。

Interface 跟 contract 最大的差別在於:interface 是純粹的介面,不涉及實作後的行為,也就是說按照 interface 實作的 client code,只確保能呼叫得到,不能確保呼叫後的行為是否符合預期;但是 contract 是包括 precondition 與 postcondition,比 interface 更進一步限制了實作行為。

但這也並不代表 contract 就能完全涵蓋 interface,主要因為 contract 通常並不涉及實作的語言;例如 interface 可以指定 method arguments 的順序跟型別,但這些通常不會涵蓋在 contract 裡頭。也因此當我們談 test code generation 時,interface 扮演主要角色,而 contract 則在 test case (input/output) generation 扮演主要角色。

最後是 API document ...
API document 主要目的是讓編寫 client code 的人能正確地產生參數呼叫 API,目的在於彌補 interface 的不足,一般會先解釋一下這個 API 的用途,加註各個參數 domain 上的意義而不單只是型別,另外也會描述拋出例外的情況等;因此就意義上偏向 contract 更多一點,但又比 contract 多依賴了特定 programming language 或 protocol 等;甚至可以包括所有 contract 的資訊形成 API contract。

近年來因為 microservice architecture 的大量興起,讓 Web API 又受到重視,許多 interface description language (IDL), API description language, API documentation language 也相繼產生,釐清上面三個的角色跟功能,可以幫助我們回頭檢視這些 description languages 應該有的功能跟後續自動化的可能。


see also:
[1] API Design Myth: Interface as Contract
[2] An API Definition As The Truth In The API Contract
[3] Design by Contract

2017-03-28

Tagging System Design

Tagging 也算是目前系統中常見的功能,實作上也被討論了不少,大致上分底下 3 種類型:
  1. Denormalized solution
    Pros:
    • 單一 table 易於直接人工閱讀資料與維護。
    Cons:
    • 所有 tag value 集中於一個 cell,需要保留一特殊字符作為分隔使用。
    • 查詢時將大量使用 LIKE,查詢條件字串組成受限;但效能上可能優於 JOIN [1]。
  2. Entity-Relation solution
    Pros:
    • 使用獨立的 table 專門紀錄 tag value 與 entity 對應的關係。
    • 個別 tag value 被拆分為獨立記錄,不需要保留特殊字符,也不會大量使用 LIKE 運算。
    Cons:
    • 必須嚴格確保每一個 entity id 與 tag value 組合只會存在一筆記錄;否則使用 HAVING-COUNT 查詢時會出現錯誤。
    • Tag 本身無法帶有其它屬性,例如:引用次數/日期、從屬關係、有效與否、與內部/顯示名稱等。
    • Tag 更名時需要更新大量資料。
  3. Intersection Table solution
    Pros:
    • 所有的 tag value 獨立一個 table 存放,易於統整 tag value 或更名。
    • Tag 本身被視為另一個獨立的 entity,可自由定義屬性。
Cascade Updates: 當使用 normalized solution 時,寫入時需要 cascade updates 往往是需特別注意的地方,但只要程式邏輯無誤,便不會產生 orphan relation/entity 的情形。

Tag vs. Category

  • Tag 是 many-to-many 的關係,category 是 one-to-many。
  • Tag 一般不實作階層式結構。階層式結構必須搭配 recursive search 才有效果,也就是以 parent tag 搜尋時,結果應包括與 child tag 關連的項目;但一般像 GMail 雖然表面上具備階層式的 label,實質上卻沒有 recursive search 的能力,屬於 一種 faux hierarchical tagging。

另外 [2] 提到 3 種常見的 tag 使用情境:(1) 使用 tag 搜尋;(2) 透過某一文件的 tags 尋找擁有類似 tags 的其他文章;(3) 透過一群 tags 搜尋其他相關 tags。

see also:
[1] Tags: Database schema
[2] In Lieu of the Promised Article on Tags and SQL

Windows Name Resolution

名稱解析 (name resolution) 雖然是存在已久的機制,但有時候還是會遇到一些因為實作差異而導致行為不同的地方。一般在 Windows 下常用的解析工具大概有 nslookup 跟 ping,撇除 single-label server name (a.k.a. Windows host name) 隸屬於 NetBIOS/WINS 的管轄範圍,當使用這兩個工具作一般網域名稱解析,需要注意的有:

  • Windows 內建 DNS Client 服務 (service name: Dnscache),會讀取系統網路介面 (network interface) 的 DNS 設定,負責 DNS cache。
  • 當變更時網路介面 DNS 設定,DNS Client 並不見得會立即套用新設定,cache 資料也可能不會 flush。
  • ping 解析時會先向 DNS Client 查詢,因此單獨變更 DNS 設定可能導致 ping 仍然讀取到舊資料;nslookup 不透過 DNS Client 因此會直接套用最新設定查詢。
  • 可以透過系統管理介面,重新啟動 DNS Client 服務,達到套用新設定與 flush cache 的目的;單獨 flush DNS cache 可使用指令 ipconfig /flushdns
  • 當 DNS Client 無該筆資料時,DNS Client 會根據 DNS 設定向外查詢,並將結果寫回 cache;nslookup 則無 cache 機制。
  • 在 nslookup shell 中變更 default server 只會暫時有效,並不會寫回系統。
  • 重啟網路介面時,亦會會重啟 DNS Client。
  • %Systemroot%\System32\Drivers\Etc\hosts 檔案為 DNS Client cache 預載內容。
see also: ping vs. nslookup

2017-03-10

Open Session in View

突然想起以前設定過 Struts2 中某個 filter,順手把之前的筆記再重新整理釐清。

Entity Manager and Transaction Manager
  • 簡單來說,entity manager 關係到資料讀取,而 transaction manager 關係到資料寫入。
  • 但兩者並非完全脫鉤,例如:在一個 transaction 中可能要參照其他 entity 的資料,或讀取 entity 時要避免 dirty data。
  • 因此 entity 要負責追蹤資料的狀態 (entity state),提供 transaction 中使用。

Open Session in View (OSIV)
  • Open Session in View 中的 session 指的是 Hibernate session
  • 用意在於避免 view rendering 的過程中,Hibernate session 過早結束導致後續 data lazy loading 產生 LazyInitializationExceptions
  • 換言之,Open Session in View 拉長 Hibernate session 的時間,並交由 view request 控制。
  • 值得注意的是:Hibernate session 結束與否不同等於 transaction commit or rollback;在純粹讀取資料的應用,操作是不需要 transaction 保護的,若是讀寫混合,Hibernate session 關閉的時機涉及 transaction 如何向 session factory 取得 Hibernate session,getCurrentSession() or openSession(),前者由 SessionFactory 自動控制,後者才需要手動在 commit or rollback 後關閉。

主張 OSIV 為 anti-pattern 的觀點集中於:
  • OSIV 忽略 readonly 與 read-write 操作本質上的不同,不應該都由 entity 決定一次存取的資料量。
  • 直接拉長 Hibernate session 給予底層實作傾向 lazy fetch 的空間,嚴重將導致 (n+1) select issue。
  • 精細一些的作法,應該由 view 決定底層應該一次 fetch 哪些欄位的資料,使用 DTO projection,而非單純由 entity 決定一次取回的欄位數量。
  • 但此種作法將提高 view 與底層 DTO 的耦合;傳統 view 只依賴 domain model,DTO 與 view 之間互不依賴。
  • 注意:這邊指的一次存取資料量,與分頁查詢無關,分頁查詢的 query 會帶有 order 與起始/結束的 row number。
Hibernate Session and Entity Manager
  • Entity Manager 是 JPA 定義的名詞,而 Hibernate session 及 SessionFactory 則是 Hibernate 特有。
  • 一般起始的順序是:transaction manger, entity manager, Hibernate session,透過 entity manager 控制 session,避免手動控制。
see also:

2017-01-26

Data Backup and Redundancy

接觸 IT 之後才發現很多人對 redundancy 跟 backup 之間的分野是混淆的,這種疑問就像前幾年 high availability 很熱門的時候,不少人都認為都已經有 data redundancy 了,還需要作備份嗎?

這種疑問通常忽略了萬一系統寫入異常或中毒的狀況,單純的 data redundancy 並沒辦法取回之前的資料狀態。

這篇文章寫的還不錯,提出了幾點:

  • Data redundancy and backups are both intended to prevent data loss, but the two technologies are quite different.
  • Data redundancy often takes the form of a synchronized copy of the organization’s data.
  • Data redundancy can help to prevent service outages.
  • Backups, on the other hand, are copies of data and other resources.
  • Backups are also a good choice for granular recovery (recovering a single file, email message and so on). In contrast, redundant systems are better suited to situations in which the organization needs to keep critical systems online and cannot tolerate a long recovery period.
相關於 backup 與 redundancy 的字眼分別有:
  • Backup: recovery, point-in-time, versioning, mean time to recovery (MTTR), revert, offline
  • Redundancy: failover, hot spare, online synchronizing, performance, reliability
Availability 是從使用者的角度出發,無論系統是忙碌或是失效,對使用者都是 not availability,所以 availability 這字眼會同時涵蓋 backup 與 redundancy。