
通過(guò)本系列文章,我試圖通過(guò)一個(gè)簡(jiǎn)單的UC來(lái)構(gòu)建我的架構(gòu)世界觀和方法論,其中核心的線索是SOC,使用的語(yǔ)言是
Scala,編程范式為FP
第一篇 編譯器的歸編譯器,運(yùn)行時(shí)的歸運(yùn)行時(shí)
無(wú)論是2C 還是 2B,用戶都是公司的命根子,當(dāng)用戶被千方百計(jì)吸引過(guò)來(lái)時(shí),我們一定不希望用戶被一個(gè)不穩(wěn)定的用戶注冊(cè)服務(wù)拒之門(mén)外。
那如何構(gòu)造一個(gè)穩(wěn)定的用戶注冊(cè)服務(wù)呢?
這還不簡(jiǎn)單么
從業(yè)務(wù)角度,這確實(shí)沒(méi)什么難的,我們可以簡(jiǎn)單的提煉出一個(gè)Case:
UC 1. 用戶注冊(cè)/Register
流程:
1. 校驗(yàn)用戶提交的注冊(cè)要素(手機(jī)號(hào)碼,驗(yàn)證碼,登陸名稱(chēng),密碼)
2. 保存用戶注冊(cè)信息,并返回成功注冊(cè)的用戶信息
2.1 如果用戶注冊(cè)要素校驗(yàn)不通過(guò),則返回注冊(cè)要素不合規(guī)范的錯(cuò)誤信息
2.2 如果保存失敗,則返回服務(wù)端暫時(shí)不可用的錯(cuò)誤信息
前置條件:
1. 通知服務(wù)已經(jīng)發(fā)送該手機(jī)號(hào)碼的驗(yàn)證碼,并提供驗(yàn)證碼驗(yàn)證服務(wù)。
后置條件:
1. 注冊(cè)成功后,用戶可通過(guò)登錄名和密碼登錄登錄以獲取服務(wù)。
基于業(yè)務(wù),我們很容易建模:
/** 用戶信息
* @param mobile: 手機(jī)號(hào)碼
* @param otp: 驗(yàn)證碼
* @param loginName: 登錄名
* @param password: 密碼
*/
case class User(mobile: String, otp: String, loginName: String, password: Vector[Char])
/** 用戶服務(wù)
*
*/
trait UserService {
/** 注冊(cè)用戶,完成 UC1 的業(yè)務(wù)規(guī)則
*/
def register(user: User): Unit
}
我們可以把這些交給Coding小伙伴來(lái)實(shí)現(xiàn)了吧:
class UserServiceImpl extends UserService {
/** 注冊(cè)用戶,完成 UC1 的業(yè)務(wù)規(guī)則
*/
def register(user: User): Unit = {
// 校驗(yàn)手機(jī)號(hào)碼格式
if (! validateMobile(user.mobile)) throw new Exception("手機(jī)號(hào)碼格式錯(cuò)誤")
// 調(diào)用驗(yàn)證碼驗(yàn)證Restful服務(wù)
if (! validateOtp(user.otp)) throw new Exception("驗(yàn)證碼錯(cuò)誤")
// 校驗(yàn)用戶登錄名格式
if (! validateLoginName(user.loginName)) throw new Exception("用戶名格式錯(cuò)誤")
// 校驗(yàn)用戶登錄名是否沖突
if (! hasExisted(user.loginName)) throw new Exception("用戶名沖突")
// 校驗(yàn)密碼強(qiáng)度
if (! validatePassword(user.password)) throw new Exception("密碼強(qiáng)度不符合要求")
// 持久化用戶信息
persistUser(user)
}
private def validateMobile(mobile: String): Boolean = ???
private def validateOtp(otp: String): Boolean = ???
private def validateLoginName(loginName: String): Boolean = ???
private def hasExisted(loginName: String): Boolean = ???
private def validatePassword(password: Vector[Char]): Boolean = ???
private def persistUser(user: User): Unit = ???
}
So far, so good!
減少編碼錯(cuò)誤
上線后,這段代碼正常運(yùn)行了一段時(shí)間,沒(méi)有出現(xiàn)啥問(wèn)題,Good!
But,But,在一次上線后,突然發(fā)現(xiàn),所有的用戶無(wú)法注冊(cè)了,What ?。?!
在比較代碼變更時(shí)發(fā)現(xiàn),構(gòu)建User對(duì)象時(shí),一個(gè)小伙伴無(wú)意中將otp參數(shù)賦給了loginName!編譯、發(fā)布,一切正常,但在運(yùn)行時(shí),校驗(yàn)不通過(guò),所以捅了大簍子!
當(dāng)然,我們可以通過(guò)代碼之外的手段來(lái)減少這種錯(cuò)誤,比如測(cè)試。但一些常規(guī)更新中,很可能漏掉無(wú)關(guān)的一些功能的測(cè)試,從“反求諸己”的原則自我要求的話,我們必須檢視,有沒(méi)有針對(duì)這種錯(cuò)誤的改進(jìn)的空間?我們的代碼是否有足夠的自我防御能力,盡早發(fā)現(xiàn)這種錯(cuò)誤呢?
一種可選的答案是Typeful 。也就是強(qiáng)類(lèi)型且類(lèi)型完全的,讓編譯器對(duì)類(lèi)型進(jìn)行檢查,在編譯期間就為我們排出這種低級(jí)錯(cuò)誤。
讓我們?cè)俅螌徱曃覀兊拇a,它是否真的反應(yīng)了業(yè)務(wù)?業(yè)務(wù)用例說(shuō),用戶注冊(cè)要素包含手機(jī)號(hào)碼、驗(yàn)證碼 等,再看看我們的User類(lèi)是怎樣對(duì)這兩個(gè)要素建模的。發(fā)現(xiàn)差異了嗎?loginName 和 otp 兩個(gè)參數(shù)都是String類(lèi)型, 我們用屬性名稱(chēng)來(lái)建模,而沒(méi)有使用類(lèi)型來(lái)建模!然而編譯器不會(huì)檢查變量名,但會(huì)檢查變量的類(lèi)型(Scala是強(qiáng)類(lèi)型語(yǔ)言)。
讓我們充分利用強(qiáng)類(lèi)型,讓編譯器替我們干最臟最累的活吧!Let's Do it!
/** 手機(jī)號(hào)碼
*/
case class Mobile(value: String) extends AnyVal
/** 手機(jī)驗(yàn)證碼
*/
case class OTP(value: String) extends AnyVal
/** 登錄名
*/
case class LoginName(value: String) extends AnyVal
/** 密碼
*/
case class Password(value: Vector[Char]) extends AnyVal
/** 用戶
*/
case class User(mobile: Mobile, otp: OTP, loginName: LoginName, password: Password)
嗯,這樣一來(lái),我們的代碼更加類(lèi)型安全了!不會(huì)再出現(xiàn)錯(cuò)傳參數(shù)的低級(jí)錯(cuò)誤了。因此,我們要盡可能的Typeful,讓編譯器檢查低級(jí)錯(cuò)誤。
更深一層考慮,我們?cè)谧鲆患虑椋?strong>SOC(Separation Of Concerns), 分離的是什么呢?我們分離的是編譯和運(yùn)行,充分利用編譯的類(lèi)型檢查職責(zé),避免將類(lèi)型的檢查延遲到運(yùn)行時(shí)!之前的代碼很明顯沒(méi)有意識(shí)到這種分離。(除了使用變量名稱(chēng)來(lái)指稱(chēng)業(yè)務(wù)含義,我們經(jīng)常犯的錯(cuò)還包括在運(yùn)行時(shí)對(duì)對(duì)象進(jìn)行類(lèi)型檢查,根據(jù)對(duì)象的類(lèi)型決定業(yè)務(wù)的走向)
這種重構(gòu),在實(shí)際的開(kāi)發(fā)過(guò)程中出現(xiàn)過(guò),比如在開(kāi)發(fā)加解密工具包時(shí),一開(kāi)始對(duì)所有的參數(shù)都是用
Array[Byte]類(lèi)型,導(dǎo)致外部調(diào)用方經(jīng)常講公鑰與私鑰參數(shù)順序搞反了,自己的單元測(cè)試完全通過(guò),但集成到其他模塊時(shí)就出錯(cuò),查這種錯(cuò)花了不少時(shí)間,可謂是教訓(xùn)深刻!