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-30
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)
然而對於 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 這個名詞,內容可能包括:
然而就 .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
- 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)
然而就 .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:
Standards: Encoding Rules & Formats
接下來可能會遇到各種檔案,這裡頭牽涉的標準主要有 2 類:
上頭這些標準名稱都常見於副檔名,譬如說:
最後是這些 key 如何被保存到 client 與 server;對系統來說,存放位置分為 TrustStore 或 KeyStore,分別存放扮演 consumer 與 provider 時,使用的 public certificate 與 private keys。
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
- Public key: 用於加密與驗證簽章
- Private key: 用於解密與產生簽章
Standards: Encoding Rules & Formats
接下來可能會遇到各種檔案,這裡頭牽涉的標準主要有 2 類:
- Encoding rule: 說明內容要如何編碼,主要有 DER 與 PEM [2] 兩種,前者的輸出為 binary,後者為 base64。
- Message or file format: 定義檔案應該包括那些資訊,主要有 PFX, P12 (PKCS #12), CSR (PKCS #10), X.509
File Name Extensions
- 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 併用。
上頭這些標準名稱都常見於副檔名,譬如說:
- *.pfx 與 *.p12 一定是 CA-signed certificate,未指定 encoding rule,但通常為 binary,可與 PEM 合併使用。
- *.p7c 一定是 public certificate,固定為 BER encoding。
- *.der 與 *.pem 通常僅為 public certificate,用副檔名描述編碼方式。
最後是這些 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 進行設定。
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
- 首先要進到 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。
設定的重點不外乎:service class 本身以及相關需要存取的物件欄位,因此只要分別在 Apex Class 與 Objects 中將相關權限打開即可。
- Production Sandbox Org: 分成 Apps 與 System 兩段,需設定的連結落在 Apps 裡頭,分別是 Apex Class Access 與 Object Settings。
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 屬性。
ps. 回傳的 charset 預設皆為 UTF-8,不需要額外設定 Accept-Charset。
see also:
[1] Apex REST Methods
[2] How can I return JSON object from REST service?
但由於回傳 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。
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,沒有細部資料,但可用於比對 (==),而後者才是真正具備細部資料的結構。
see also:
[1] Apex Developer Guide: Schema Class
[2] Apex Developer Guide: Schame Namespace
[3] Apex Developer Guide: Dynamic Apex
- Token 轉換至 describe result 的行為稱之為 describe,呼叫 getDescribe() method,可視為 dereference & data loading。
- SObject 與 field 的 token 與 describe result 資料型別與相關取用方式分別為:
- Schema.SObjectType
- accountObj.getSObjectType()
- Account.sObjectType
- Schema.SObjectField
- Account.Name
- fieldResult.getSObjectField()
- Schema.DescribeSObjectResult
- objToken.getDescribe()
- Schema.sObjectType.Account
- Schema.describeSObjects(new String[] {'Account'})
- Schema.DescribeFieldResult
- fieldToken.getDescribe()
- objResult.fields.Name
- 除了 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:
值得注意的是:APEX REST annotations 中只有 RestResource 才有 urlMapping 參數,且該 annotation 為 class level;換言之:
[1] Apex REST Annotations
[2] Creating REST APIs using Apex REST
值得注意的是: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 中。
[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。
值得注意的是:
因此如果我們要讓非 Web 操作也能產出多語言內容的話,有兩種選擇:
假設今天我要製作一個具備多國語系的畫面,除了預先將 template 跟 phrase 準備好存入 database 或 file system 外,主要的 rendering 邏輯如下:
首先,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。
- 取用 Language Engine instance 的邏輯被放在 TemplateEngine->Get() 中,除了 Web 操作根據 cookie 在底層 SWIFT 生成不同語言的 instances 外,其餘都使用預設的 (English) Language Engine instance。
- TemplateEngine->Get() 不帶 phrase keys 參數,Language Engine 負責載入所有的 phrases,交由 Dwoo core 產出最終文本。
因此如果我們要讓非 Web 操作也能產出多語言內容的話,有兩種選擇:
- 修改 TemplateEngine->Get(),新增語言別參數,動態產出對應的 Language Engine instance。
- 在呼叫 TemplateEngine->Get() 前預先產出所需的 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 為例:
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 的錯誤訊息。
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 對新手往往是比較難懂的,主要原因包括:
下表整理了 SOQL 針對不同 side 作為起點,如何透過 relationship traversal 達到各式 JOIN 的 patterns:
ps. 上述 SOQL 中,只有在取用 relevant object 的欄位時才需要使用 __r notation,若只需該物件 id,則用 __c 即可。
[2] 說明了定義 relationship 時的命名原則:
[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
- Object modeling 時採用的 schema builder 看似 ER model,但實際使用 SOQL 操作時,卻是以物件導向的觀點。
- SOQL 沒有 JOIN,只能透過 relationship traversal with path expression 完成各式的 implicit JOIN。
- Object 中記錄 relationship 的 field naming,有獨特的 convention,有時使用 __c,有時卻要使用 __r。
- Relationship 的 cardinality 固定為 one-to-many;使用 SOQL traverse relationship 時,從 one side 或 many side 出發會需要截然不同的 SOQL statement patterns。
- 設定 relationships 可能分為兩類:lookup 與 master-detail (a.k.a. MD)。
- 以物件導向的角度,上述分別是 aggregation 與 composition;前者是 whole-part 的關係,後者有一致的生命週期。
- 文件中的 parent 與 child,並非專屬於 master-detail relationship,純粹分別用於 relationship 中的 one side 與 many side。
- 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 針對不同 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 |
[2] 說明了定義 relationship 時的命名原則:
- 在 parent object 中定義 relationship 時:名稱須 unique to the parent,並且使用複數形的 child object name 命名。
- 在 child object 中定義 relationship 時:名稱須 unique to the child,並且使用單數形的 parent object name 命名。
- 按前述第 (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
[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
傳統的 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
傳統上一般開發好的 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)
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) 追蹤功能。
上表是 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件事讓你搞清楚
這 3 個模組中,又以業務功能為 CRM 的最核心,CRM 的行銷功能往往專注於 target list、bugget planning,輔以更專業的系統負責 custom portal、multi- campaign channel management (newlettter, email, phone call, social networks)、閱讀追蹤、SEO 等功能;而客服系統也往往會拆分出 helpdesk 系統,提供更多案件 (case) 追蹤功能。
| Marketing | Sales | Customer 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
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 種類型:
- Denormalized solution
Pros:- 單一 table 易於直接人工閱讀資料與維護。
- 所有 tag value 集中於一個 cell,需要保留一特殊字符作為分隔使用。
- 查詢時將大量使用 LIKE,查詢條件字串組成受限;但效能上可能優於 JOIN [1]。
- Entity-Relation solution
Pros:- 使用獨立的 table 專門紀錄 tag value 與 entity 對應的關係。
- 個別 tag value 被拆分為獨立記錄,不需要保留特殊字符,也不會大量使用 LIKE 運算。
- 必須嚴格確保每一個 entity id 與 tag value 組合只會存在一筆記錄;否則使用 HAVING-COUNT 查詢時會出現錯誤。
- Tag 本身無法帶有其它屬性,例如:引用次數/日期、從屬關係、有效與否、與內部/顯示名稱等。
- Tag 更名時需要更新大量資料。
- Intersection Table solution
Pros:- 所有的 tag value 獨立一個 table 存放,易於統整 tag value 或更名。
- Tag 本身被視為另一個獨立的 entity,可自由定義屬性。
Cascade Updates: 當使用 normalized solution 時,寫入時需要 cascade updates 往往是需特別注意的地方,但只要程式邏輯無誤,便不會產生 orphan relation/entity 的情形。
Tag vs. Category
另外 [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
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
Open Session in View (OSIV)
主張 OSIV 為 anti-pattern 的觀點集中於:
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 並沒辦法取回之前的資料狀態。
這篇文章寫的還不錯,提出了幾點:
- 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。
訂閱:
文章 (Atom)


