2016年11月,接受了一個工作,是對“悟空CRM”進行一些修補。這是一個不錯的 CRM,開源,並提供一個 SaaS 的服務。正好微軟的 .NET Core 和 ASP.NET Core 也發佈了。於是就有了這個想法:使用 ASP.NET Core 來開發一個 CRM。當然這裡面的私心是:朝後坦白講 ...
2016年11月,接受了一個工作,是對“悟空CRM”進行一些修補。這是一個不錯的 CRM,開源,並提供一個 SaaS 的服務。正好微軟的 .NET Core 和 ASP.NET Core 也發佈了。於是就有了這個想法:使用 ASP.NET Core 來開發一個 CRM。當然這裡面的私心是:朝後坦白講,悟空CRM 的代碼真的是不怎麼樣。大量的代碼堆在 Controller 里,多個功能在一個 EndPoint 里混合。許可權管理也有些亂來。View 里充滿了“臨時解決方案”。所以我真的是一邊改,一邊難受。由於11月我還在做一個 Xarmin 的小程式,所以對 CoreCRM 的開發就定在12月開始了。
因為修改悟空CRM,本來以為對業務的邏輯已經比較熟悉,先開始的時候照著悟空CRM的UI直接開始擼就可以了。在嘗試了幾個頁面之後發現這樣比自己直接寫還麻煩。而在這中間,我的老毛病有犯了:在幾種技術方案之間不停地權衡和嘗試。這樣,時間就一天天的浪費掉了。技術方案的選擇經歷了:VueJS + jQuery,React.NET,aspnetcore-spa (ReactJS + Redux),最後又回到 VueJS + jQuery。CSS 框架使用的是 Bootstrap 3.3.6,這個是一直沒有變(雖然我也曾經想過使用4.0的alpha版,不過最後還是忍住了)。圖標使用了font-awesome 4.7.0,也是沒有改。一直折騰了一個月(這中間還有因為對 ASP.NET Core 不夠熟悉而付出的學習成本),整個12月將要過完的時候,我才只完成了 Layout 和 Login。(其實原來的首頁、結構架構也完成了,但只有UI的部分)。
關於技術選擇
為什麼要選擇 ASP.NET Core?
我的一個基本判斷是:帶有類型檢查的語言應該是未來的趨勢。雖然從歷史角度看,動態類型和靜態類型總是交替上臺表演的。不過,隨著程度規模的不斷變大(想當年一個 DOS 程式就幾十,幾百K,求伯君可以使用彙編擼一個 WPS 出來,而現在一個手機 App 也是幾十MB),動態語言的一些不方便的方面是突顯出來了。特別是多人協作開發的時候,因為沒有類型的靜態類型檢查,很多錯誤都只能在運行的時候才能發現。其實這個問題如果配合上足夠的單元測試,也是可以減輕一些的,然而到了2016年,還是有很多人把測試當成負擔。結果就是大量的 bug 和安全漏洞,以及改了補了一個洞,又開了三個洞。
從現實情況看,PHP 7 已經引入了一些類型標註,Python 3.5 也有這樣的東西,JS 系裡有 TypeScript 這樣的 Transpiler(而且,Angular 2 這樣的框架已經開始在使用 TypeScript 進行開發了),以及 Facebook 的 flow。所以,動態語言在漫漫的靜態化。雖然只是提供了類型的運行前檢查,但也減少了很多運行時的問題。
那麼,現在比較成熟的靜態類型語言,我知道的就是三個:C++、Java 和 C#。C++ 是出了名的複雜和開發效率低下。雖然我對 C++ 的熟悉程度比 C# 要高的多,但 C++ 做 Web 的挑戰還真是大得讓我不敢嘗試。Java 雖然現在是 TOIBE 上排前三的語言,也一度占到了榜首好多年,但我還是不太喜歡 Java。我覺得 Java 寫進來太死板,太多的架構代碼,感覺很瑣碎。最後我選了 C# 這個 sugar language。然而,其實我也不太會 C#,今年並沒有安排學習 C# 的任務。只是年初的時候因為學習 F#,所以學了一點點的 C#。但用起來還真的不錯。同時,微軟終於開眼,開始與開源社區深入合作,而不是把開源當成敵人,於是有了 .NET Core 這個跨平臺的 Runtime。
遇到一個問題
中間的一次挫折,差點讓項目中斷。ASP.NET Core 的 ORM 使用的是 Entity Framework Core,而 EF Core 基本上是對固定的模型比較好用。而 CRM 這種需要定製的系統,需要對一些表進行定製(比如一個客戶需要保存哪些信息,這不可能有一個通過的模板)。我嘗試了幾種可能的方案,都不能在 EF Core里實現動態的模型。本來以為自己又一次做錯了架構選擇,不過,退後一步想,我為什麼非要使用 EF Core呢?然後去搜了一下,發現 StackOverflow 的 Dapper.NET 可以支持動態的 model。於是這個危機才算解除。
前端框架選哪個?
因為後端的技術選擇了 ASP.NET Core,這就對前端的技術選擇造成了一定的限制。如果我是使用 PHP 或者 Python 開發的話,我可能會使用前後端分離的技術。但 ASP.NET Core 對 HTML 這個層做了一些的工作(我想JSP可能也是這樣的),比如根據運行時的環境選擇載入不同的 JS 和 CSS 文件(還支持 CDN 的 fallback,非常貼心);支持 class 的自動補全和智能提示(這個非常提升生產力),甚至還支持對 Font-awesome 的預覽;支持 Partial View 和 View Component,也就是對 View 的按功能分割也可以直接實現。所以,前端的部分其實只是做一些局部的更新。
現實局部更新最簡單粗暴的方法就是使用 jQuery。不過,jQuery 的時代已經過去了,那種 query and modify 的時代已經和現在這個 MVVM 一統天下的局面不符合了。使用 jQuery,必然還需要在 JS 里寫一些難看的 HTML。所有的結果就是導致代碼的維護成本高起。而在 2016 年,我們有什麼樣的前端框架可以選擇呢?首先是我最喜歡使用的 VueJS,這個其實算不得一個 framework,最多是一個 micro-framework,提供了 view model 和 model 的綁定和雙向更新;一些條件渲染和組件化。使用進來非常簡單和直接。可我為什麼還要配合上 jQuery 呢?主要是因為要使用 bootstrap 的一個 JS 組件。但在直接使用 VueJS + jQuery 這個方案的時候,還是有一些問題。因為我沒有做伺服器端渲染(SSR),所以頁面在打開的時候總會閃一下,那個是 VueJS 對模板重新編譯再插入到 DOM 造成的。好像 VueJS 1.x 的時候沒有這個問題,2.x 引入了 virtual DOM,好像也引入了這個問題。解決方法比較直接的就是SSR,不過,我準備使用非同步組件的方式來做。
ReactJS一直是我想嘗試的一個前端框架。因為不但可以在 Web 里使用,還可以把同樣的經驗轉移到 React Native 的開發中。比如 Route、Rudex 等都是相通的。同時 React 也直接實現了 SSR,解決了前面說的問題。為了嘗試 ReactJS,我前後嘗試了兩個技術:ReactJS.NET 和 aspnetcore-spa。前者是 Facebook 出品,後者是微軟出品。ReactJS.NET的優勢是:有一些 Tag Helper,可以和 ASP.NET Core 的 View 整合的比較好。但遇到的一個問題是,在 macOS 上沒法運行。雖然我已經提了 issue,和一個 pull-request,但問題好像不會在短時間內解決。只好先放棄這的方案。
微軟自己搞的 aspnetcore-spa,只可以在各種平臺運行的。但使用的時候發現:1. className 沒有 class 的補全功能;2. Server-Side 的驗證還沒法整合(雖然在 README 里寫了這條,但沒有 docs,不知道怎麼用)。
也就是說,ReactJS 的兩種方案,就算可以使用,也因為破壞了 ASP.NET Core 的 View 直接提供的功能而在使用的時候不免遇到各種問題。而嘗試這兩種方案,消耗了我兩周的時間(要研究怎麼用,發現問題,解決問題,與社區溝通……)
最後,還是回到了 VueJS + jQuery 的道路上來了。不過,使用方法做了一些改變。最初的使用方法是:在 body 下麵創建一個 div 做為所以內容的 container,然後把各種 modal, list 都扔進去,掛上 attribute。這樣的結果就是:因為有好多的 modal,和一些 list,結果整個模板變得很大,編譯的時候就會閃。新的方法是,使用 Vue 的動態組件的功能,在需要載入的時候,通過一個獨立的 Controller 載入到頁面,雖然在載入的時候會顯示一個 Loading,但因為每個組件都不大,應該不是什麼大問題。
公開的開發過程
CoreCRM 選擇使用 Apache 2.0 協議開源(其實我也不知道應該選什麼,MIT、BSD 好像都不錯的樣子)。反正代碼都開源了,開發的過程我也開放出來好了。這裡我會記錄每一次開發的思考、決策和反思。
如前所述,目前 CoreCRM 還在初級階段,只有登錄和登錄的做成測試。不過,在這篇文章這後,項目會慢慢展開。
持續集成
在12月初的開發中,我並沒有引入持續做成。一開始的三天是在混沌中度過的。然後我決定先寫 UI(HTML和CSS),然後再把後端完成,最後前後端集成。實踐的結果是:因為一直在寫UI,所以感覺一直沒有什麼進展,除了登錄,什麼功能也不能使用。這樣的感覺很不好。特別是只有我一個人在開發的時候,更需要一些小的成功來激勵我繼續前進。所以,在 12 月的第四周,我開始研究怎麼使用持續集成(因為離發佈還很遠,所以,所有的發佈功能都被關閉了)。現在已經接入了 Travis-CI 和 AppVeyor 兩個 CI 平臺。登錄功能的測試也已經通過。
代碼庫和項目管理
這裡有一個問題:因為我是使用 Coding.NET 做為主要的版本庫的(因為這個服務是在國內,速度上比 GitHub 要快一些),但 Travis 並不支持非 GitHub 的 Git Repo,所以,只好再在 GitHub 上開一個鏡像。雖然 AppVeyor 可以直接使用 Coding.NET,我也沒配置這個功能(只是嘗試了一下,然後刪的時候刪錯了……)現在兩個 CI 都配置在 GitHub 上了(那你幹嘛不直接就在 GitHub 上搞?)。反正現在只有我一個人搞,這還不是一個問題。如果以後有其他人一下參與,我可以做一下同步的功能,自動把 Coding.NET 的代碼同步到 GitHub 上進行測試。
使用 Coding.NET 的一個原因是,Coding.NET上還支持一些簡單的項目管理。這樣,可以直接在上面寫任務,完成任務,也是一種項目開發的體驗。如果以後有多人參與了,在持續集成方面,我還想把 Worktile 拉進來(測試完成後的提示等)。
邀請
如果你也對 ASP.NET Core 和 .NET Core 的未來感興趣,想參與到這個項目中來,歡迎到 Coding.NET 上關註這個項目。在這裡你將能學到:
- ASP.NET Core
- VueJS
- 單元測試
- 集成測試
- More
項目地址:
https://coding.net/u/holmescn/p/CoreCRM/git
https://github.com/holmescn/CoreCRM