<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      使用函數式語言來建立領域模型--類型組合

      使用函數式語言來建立領域模型

      領域模型=代碼=文檔

      如果說敏捷軟件開發主張面對面溝通,通過快速迭代的手段,讓有價值的軟件盡早面向市場,從而適應快速變化的需求。

      那么DDD則為敏捷開發過程中的溝通形式作出了進一步的補充,DDD讓領域模型和代碼以及文檔之間畫上了等號,主張讓代碼成為團隊之間溝通和交流的途徑。縱觀DDD的所有環節,無一不是在打通領域專家和開發人員之間的溝通和交流,而代碼無疑是最有效,最實時的共享模型。
      DDD的精髓在于通過讓開發人員理解領域,進而讓開發人員使用編程語言建立一個跟領域專家腦海中一致的領域模型,使得該領域模型成為大家共享知識的途徑,這將有效的減少不同利益相關者的溝通及交流,確保所有人都在解決同一個問題。

      領域建模

      領域建模是整個DDD環節中最最考驗開發人員功底的一環,不同于傳統的數據庫建模技術,開發人員需要有很好的抽象能力,通過恰如其分的編程技術,將領域知識映射到一個代碼模型中。
      長期以來OO語言被認為是領域建模的首選,一些OO的技巧可以很好的用來抽象領域模型。而函數式語言則被普遍認為只能用來做數據處理,科學計算等。本文將為大家展示如何通過函數式編程語言進行領域建模,本文選用TypeScript編寫實例,TypeScript類型系統完全滿足函數式編程需求,當然本文也適用于其他擁有靜態類型系統的函數式編程語言。

      TypeScript的類型系統

      實際上你只需要知道少量的知識就可以開始領域建模了,從這個角度來講,實際上函數式類型系統更適合領域建模,從而讓領域模型成為文檔。

      類型

      各類編程語言在設計的時候就已經提供了類似string, bool, number等簡單類型(primitive),然而在真實世界里面,你還需要將這些類型組合成更大的類型,從而來映射現實世界。
      在TypeScript中,type關鍵字用來組合更大的類型:

      type Name = {
        firstName: string
        middleName: string
        lastName: string
      }
      

      上面類型的用途是顯而易見的,除此之外type還有起別名的用途,不要小瞧這個特性,他可以幫助你把領域知識記載在你的領域模型中,考慮下面的代碼:

      const timeToFly = 10
      

      你能一眼看出這句代碼代表的領域知識嗎?也許不能,fly多久?查文檔?No,你應該時刻告訴自己,代碼等于文檔。改進后的代碼如下:

      type Second = number
      const timeToFly: Second = 10
      

      Or類型

      OO語言無法創建這種類型,在TypeScript,這種類型被稱為聯合(Union Types),通過符號|來創建,考慮下面的類型:

      type Pet = Fish | Bird
      

      PetFish或者是Bird類型。一般來說函數式語言都會有強大的模式匹配能力,來處理這種類型,然而受制于TypesScript沒有模式匹配或者說能力很弱,通常情況下,會在類型里面添加一個字符串字面量, 從而來區分不同的類型, 在次不再細說。

      And類型

      在Typescript中,這種類型被稱為交叉類型(Intersection Types),通過符號&來創建,考慮下面的類型:

      type ABC = A & B & C 
      

      表示ABC類型包含所有A、B、C三個類型里面的屬性。

      定義函數類型

      在TypeScript中,函數與其他類型沒什么區別,也可以通過type關鍵字來定義,例如:

      type Add = (a: number) => (b: number) => number
      

      Add是一個函數,接收兩個類型為number的類型a和b,返回number。

      通過代碼來共享領域知識

      type CreditCard = {
        cardNo: string
        firstName: string
        middleName: string
        lastName: string
        contactEmail: Email
        contactPhone: Phone
      }
      

      通過前面介紹的知識,我們很容易就可以寫出上面的代碼,用來描述CreditCard這種支付方式。注意我們沒有使用class
      但這是一個靠譜的領域模型嗎?如果不靠譜,它的問題在哪里?
      這段代碼最大的問題是他沒有把本該擁有的領域知識記錄在其中,我來試著問你幾個問題:
      問:middle name可以為空嗎?
      答1:不清楚,也許需要查文檔。
      答2:也許可以吧?middle name可以為null

      為可空類型建模

      在函數式編程語言中,可空類型被定義為Option,雖然null在ts中是合法的(注:我們可以通過strictNullChecks來強致null檢查),但是在函數式編程語言中,你只能通過Option類型來表達可空類型。
      當領域專家告訴你:middle name可以存在,或者為空。注意用詞,說明我們可以通過Union類型來為可空類型建模。

      type Option<T> =  T | null
      

      一個簡單的Option其實就是一個類型, 當然你可以使用一個更加復雜的Option實現, 不過不在我們今天的討論范圍內。經過修改后的代碼變成了這樣:

      type CreditCard = {
        cardNo: string
        firstName: string
        middleName: Option<string>
        lastName: string
        contactEmail: Email
        contactPhone: Phone
      }
      

      避免基本類型偏執(Primitive Obsession)

      問:cardNo可以用string來表示嗎?如果是,它可以是任意字符串嗎?firstName可以是任意長度的字符串嗎?很顯然,你無法回答上面的問題,源于這個模型并沒有包含有此類領域知識。
      也許在編程語言里面,cardNo可以用string表達,但是cardNo在領域模型中,string無法表達出cardNo的領域知識。
      cardNo是一個200打頭的19位字符串,name是一個不超過50位的字符串,這樣的領域信息可以通過type alias來實現:

      type CardNo = string
      type Name50 = string
      ...
      

      有了上面兩個類型,你就有機會通過定義函數的方式,將cardNo業務規則包含在領域模型中。

      type GetCardNo = (cardNo: string) => CardNo
      

      如果用戶輸入了一個20位的字符串,函數GetCardNo返回什么?null?拋出異常?實際上函數式編程語言有比異常更加優雅的Error handling方式, 例如Either Monad或者Railway oriented programming。本文雖然不包含這類話題,但至少目前我們可以用Option來表示這個函數簽名:

      type GetCardNo = (cardNo: string) => Option<CardNo>
      

      這個函數類型清晰的表達了整個驗證過程,用戶輸入一個字符串, 返回一個CardNo類型,或者空。修改后的領域模型變成了這樣:

      type CreditCard = {
       cardNo: Option<CardNo>
       firstName: Name50
       middleName: Option<string>
       lastName: Name50
       contactEmail: Email
       contactPhone: Phone
      }
      

      于是,現在的代碼擁有跟多的領域知識,豐富的類型還充當了單元測試的角色,例如,你永遠都不會把一個email賦值給contactPhone,它們不是string, 它們代表不同的領域知識。

      領域模型的原子性和聚合性

      這個領域模型中的三個name可以分別修改嗎?例如只修改middle name?如果不可以,如何將這種原子性的修改知識包含在領域模型中?
      實際上我們很容易就能把NameContact兩個類型分離出來并加以組合:

      type Name = {
        firstName: Name50
        middleName: Option<string>
        lastName: Name50
      }
      
      type Contact = {
        contactEmail: Email
        contactPhone: Phone
      }
      
      type CreditCard3 = {
        cardNo: Option<CardNo>
        name: Name
        contact: Contact
      }
      

      Make illegal states unrepresentable

      在領域建模過程中,這是一條非常重要的原則,用通俗的話可以理解為:你建立的領域模型應該有盡可能多的靜態檢查和約束,讓錯誤發生在編譯時,而不是運行時,從而杜絕犯錯誤的機會。其實整個領域建模都是在遵循這個原則,例如上面的Email類型和Phone類型,為什么不用string來表示呢?因為string給與的領域知識不夠,從而允許開發人員有了犯錯誤的機會。
      讓我們最后看一個例子,用來說明這條原則如何被應用在領域建模中。 上面領域模型中有一個contact類型,包含一個Email和Phone屬性。支付成功后,系統可以通過這兩個屬性給用戶發通知,由此延伸出來這樣一條規則:用戶必須至少填寫一個Email或者一個Phone來接受支付消息。
      首先,上面的領域模型是不匹配這條業務規則的,因為Email和Phone類型都是非空類型,意味著這兩個屬性都應該是必填項。
      我們能不能把它倆都改為Option類型呢?

      type Contact = {
        contactEmail: Option<Email>
        contactPhone: Option<Phone>
      }
      

      顯然也不行,實際上就是違反了Make illegal states unrepresentable, 給與了代碼犯錯的機會,你的領域模型表達出了一種非法的狀態,即Email和Phone都可以為空,你也許會說我的xxService做了驗證呢,它倆絕對不會同時為空。對不起,我們希望我們的領域模型能夠包含這種領域知識,至于xxService,跟領域模型無關。到底能否將這一規則表達在領域模型中嗎?答案是肯定的,規則中有一個字,即我們可以通過Or類型(union)來表達這種關系:

      type OnlyContactEmail = Email 
      type OnlyContactPhone = Phone
      type BothContactEmailAndPhone = Email & Phone
      
      type Contact = 
        | OnlyContactEmail
        | OnlyContactPhone
        | BothContactEmailAndPhone
      

      結束語

      本文旨在通過函數式編程語言來指導領域建模,整個代碼示例中沒有出現類或者子類,更不會出現abstract, bean等關鍵字,衡量一個領域模型的好壞取決于
      1)領域模型是否包含了盡可能多的領域知識,能否反映領域專家腦海中的業務模型
      2)領域模型能否成為文檔,進而成為所有人溝通和共享知識的途徑
      同時,一些語言,框架的”行話“應該越少越好,例如你在領域模型中創建了一個叫做AbstractContactBase的類,除了增加復雜度,對共享領域模型這一目的幫助甚少。
      實際上函數式編程語言的類型系統,不但能夠幫助開發者建立一個豐富的領域模型,同時簡單可組合的類型系統,也為代碼即文檔提供了基礎。不可否認真實世界遠比本文所描述的例子復雜,但是大部分復雜的部分,并不會出現在領域模型中,例如函數式編程中的各種”行話“,他們往往出現在數據請求的validation, 請求第三方,數據轉化,持久化等實現階段。在未來的文章中將會描述整個http請求到領域模型再到輸出過程中如何通過函數式編程語言來實現。

      posted @ 2021-01-01 11:50  richiezhang  閱讀(891)  評論(1)    收藏  舉報
      主站蜘蛛池模板: 精品久久久久久无码中文字幕| 西西人体大胆444WWW| 18禁黄无遮挡网站免费| 免费无码又爽又刺激高潮的app| 国产成人一卡2卡3卡四卡视频| 色综合久久久久综合体桃花网| 在线a人片免费观看| 中文字幕人成乱码熟女| 色综合一本到久久亚洲91| 国产suv精品一区二区四| 亚洲欧美自偷自拍视频图片| 亚洲日本精品国产第一区| 免费VA国产高清大片在线| 国产无遮挡裸体免费久久| 久久99久国产精品66| 在线观看潮喷失禁大喷水无码| 蜜桃伦理一区二区三区| 40岁大乳的熟妇在线观看| 国产黄色看三级三级三级| 色欲狠狠躁天天躁无码中文字幕 | 任我爽精品视频在线播放| 熟女精品色一区二区三区| 成都市| 亚洲综合在线日韩av| 亚洲一区二区中文av| 欧美性猛交xxxx富婆| 亚洲经典av一区二区| 成人嫩草研究院久久久精品| 深泽县| 国产日韩av二区三区| 亚洲熟女乱色综合亚洲图片| 国产福利萌白酱在线观看视频 | 亚洲第一香蕉视频啪啪爽| 无码高潮爽到爆的喷水视频app| 潮喷失禁大喷水无码| 国内少妇偷人精品免费| 风流老熟女一区二区三区 | 亚洲精品美女一区二区| 日韩黄色av一区二区三区| 成人无码影片精品久久久| 人妻中文字幕精品系列|