成為功能管理大師:應用持續整合精神 與 Laravel Pennant 實戰工作坊
教學大綱:
先到 GitHub 上 Fork 此公開的版本庫:https://github.com/MilesChou/Mart
下載回來後,先安裝 Composer 依賴套件
複製 .env
並產生 key
設定資料庫,已有設定好 Docker Compose,可以直接透過指令啟動:
替代方案是使用 SQLite,先產生空檔案:
接著再到 .env
檔裡調整設定,主要是改 DB_CONNECTION=sqlite
以及註解 #DB_DATABASE=laravel
,其他 DB 設定不變
DB 設定完成後,即可執行 Migration
執行 PHP built-in 伺服器
打開 http://localhost:8000 即可看到首頁。打開 http://localhost:8000/admin/login 可登入後台
預設帳號如下,密碼統一為 password
建立帳號指令如下:
可以透過 Artisan 指令建帳號:
前端相關的資源已提交進版本庫。不更新前端資源的前提下,執行本工作坊可能會有一點影響,但不會到非常嚴重。
建置前端資源的方法如下:
本工作坊會介紹 Laravel Pennant 套件,與適用它的實作內容
參考 Laravel Pennant 官網內容,裡面說明寫得很詳細,工作坊的第一個內容是來介紹這個套件的功能:
類別圖
管理 Feature 的主要物件是 FeatureManager
,而產生(resolve 方法)Driver
的時候會使用 Decorator
包裝:
Laravel 設計 Manager 的習慣,是它把當作 Aggregate Root 在做的,因此所有存取背後的 Entity 都必須要經過它。
在呼叫的時候可以透過 __call()
直接 Proxy 到預設的 Driver:
最後就是 PendingScopedFeatureInteraction
,Decorator 把所有的呼叫都轉給它處理。它的用途是讓 Decorator 整合 Scope 參數的邏輯整理在這個 class,而原本的 Decorator 則是專注在實作 Driver,是一種職責分離的處理方法。(Container 也有一樣的設計)
而對廣義的程式設計來說,這是一種 Mixin 的手法:
這個結果也就讓 FeatureManager 可以同時擁有 Decorator 與 PendingScopedFeatureInteraction 所提供的方法。
但還是要回到前面提到的,Laravel 設計 Manager 的概念,就是把 Manager 當作是 Aggregate Root。
https://github.com/MilesChou/Mart/pull/7
RetrievingKnownFeature
RetrievingUnknownFeature
DynamicallyDefiningFeature
以上說明應該是完整的,但缺少使用情境,比較不容易快速理解。接著要正式開始依使用情境開發,先回顧 Flags 的四個種類如下:
接著我們把這四種開關區分成兩類:
這兩類開關設計的差異關鍵在於,常常使用的開關就是一種「功能」,因此在一開始在寫程式的時候,就能夠導入開關的概念;相反地,短暫使用的開關因為最終會移除,因此在寫程式的時候,是能夠將「分支」當作是開關在使用,切換分支即可切換關開,而合併分支則是移除開關。
依這個概念即可推測,Ops Toggles、Permission Toggles、Experiment Toggles,會是一開始就把開關就做好,而 Release Toggles 則是一開始沒有開關,而是可以後續補上。
實驗性質的功能:Didn’t Find Your Match,這是一個讓顧客在找不到商品的時候,可以知道還有什麼選擇可以用的 UX 設計。
這個功能可能很棒,但並不確定是否對我們的使用者有用,因此會是個實驗性質的功能。
我們將會把這個功能做成開關,並決定要對哪些使用者開放功能,此功能的 PR:Feature: Didn't Find Your Match by MilesChou · Pull Request #4 · MilesChou/Mart · GitHub
根據 Laravel Pennant 提供的方法,我們知道可以用 Blade 語法來達成這個目的:
一個開關除了程式判斷外,還要定義它的初始化方法,以及強制的開關方法,也將會在練習中說明。
初始化的方法,可以使用 Class,先用 php artisan pennant:feature Experiment
建立 class:
這裡有一些方案可以參考,例如:
return Lottery::odds(1/100);
$scope
就必須要限制是 User 型態,讓 Feature 有辦法判斷與選擇,像 id 是 2 的倍數可以這樣寫:
return ($scope->getAuthIdentifier() % 2) === 0;
return false
就可以了,我們可以額外寫指令來啟用開或關。
php artisan app:experiment:status
php artisan app:experiment:activate
php artisan app:experiment:deactivate
接著再來看看如何測試:
(預計會加測試練習項目)
遇到問題可以緊急關閉營運的功能:Ops Flags
另一個類似的功能是 php artisan down
,但我們這裡使用 Laravel Pennant 來達成這個目的,但不用讓整個服務下線。
首先先定義使用下面這三個指令來控制開關:
只要停用,註冊、登入、購物功能就會被隱藏起來(表面上),以讓系統能夠先把手頭上的任務完成後,再重新打開服務。
(預計會加測試練習項目)
Q: 是否可以開一個 DB 欄位存,就能達到一樣的需求。
A: 如果有 Scope 概念的話(如 login 與 order 要區分)那 Laravel Pennant 就是個不錯的選擇。
此為 VIP 會員專屬的額外打折功能。
https://github.com/MilesChou/Mart/pull/6
非 VIP 會員會是原價售出,而身為 VIP 會員,可以無條件打九折。
這跟 Ops Toggles 很像,通常會由使用者介入觸發設定的開與關(也有可能是自動的),差別在於 Ops Toggles 在意的全站功能調
這是壓軸,也是最難完成的工作坊。
首先先論三個功能同時開發的可能性:
主幹開發有提到一個概念,稱之為抽象分支(Branch by Abstraction),這是使用 Feature Flags 的概念來達到「用程式做分支」的結果,這同時也是「在飛行中的飛機更換引擎」的技術。
我們可以先試著在本機合併這些分支,它們有極高的機率會發生衝突,這正是主幹開發所提到的 distance。
此工作坊的執行步驟:
php artisan pennant:purge