#book #r2022

# なぜこの本を読んだか
[[Designing With Types]] のブログシリーズの著者である [Scott Wlaschin](https://twitter.com/ScottWlaschin) さんの本で 2018年出版だけどなぜかここ最近話題になってるのを目にしたので読んでみた
[https://motemen.hatenablog.com/entry/2022/09/domain-modeling-made-functional](https://motemen.hatenablog.com/entry/2022/09/domain-modeling-made-functional)
[https://r7kamura.com/articles/2022-10-09-domain-modeling-made-functional](https://r7kamura.com/articles/2022-10-09-domain-modeling-made-functional)
# 何が書かれている本か
[[Designing With Types]] が 2013年 で この本が 2018年出版である。
内容の中核を担うのは [[Designing With Types]] の内容でこれに加えて実装部分や DDD の精神みたいな部分が加筆されているようなイメージを抱いた。
ただ個人的には DDD に限らず、仕様を語る実装 ( [Screaming Architecture](http://blog.cleancoder.com/uncle-bob/2011/09/30/Screaming-Architecture.html) ) を実装する上でのパターンとしてモデルの状態と状態遷移図に着目するというパターンの1つをこの本では紹介しているような本だなと思う。
もちろんこれだけでドメインを意識したアプリケーションが作れるほど簡単ではないが1つの側面としては新しい観点なのかなと思ったりした。
# メモ
## DDD の精神
開発者は、コードを書くだけが仕事ではない。
ソフトウェアを通して問題を解くことが仕事であり、コードを書くのは1つの側面であって、良い設計やコミュニケーションは同様に重要である。
ソフトウェア開発をパイプライン処理に見立てたときに `garbage in, garbage out` というルールが適用される。
つまり品質の悪い入力からは良いコードは絶対に生み出されない。
`garbage in` をいかに品質をあげていくかということをやっていくのが DDD の精神であると考えている。
DDD は Domain Expert というドメインに最も詳しい人が存在する。
しかし実際に本番にリリースされるアプリケーションはあくまでも開発者が理解したものがリリースされることに注意しないといけない。
例えば、 Domain Experts -> Business Analyst -> Architect -> Development Team という形で伝言ゲームが一方的に起こった場合末端の Development Team が Domain Experts の考えを理解できるわけがない。
関係者全員の精神を揃える `Shared Mental Model` が重要である。
この精神を揃えるために様々なワークショップなどがあるがおすすめは [[Introducing Event Storming]] である。
Domain Event (アプリケーションにとって重要なイベント) を中心にそのイベントの結果どういう影響がシステム全体に発生するのかという全体像を関係者が把握することが重要となる。
## Modeling the Domain
### Functions
Fucntion は、一般化すると `入力 -> 出力` という形をしたブラックボックスと捉えることができる。
また `入力` と `出力` をさらに一般化してある型がある型に変換されることが Function と捉えることができるだろう。
Kotlin
```
// Int から Int への変換
fun add1(x: Int): Int = x + 1 // Int -> Int
```
### Types
型には2つの種類の見方がある
それが `AND Types` と `OR Types` である。 (要するに直積と直和)
Kotlin
```
// 多分ニュアンスはサラダなのでごちゃまぜという感じかな?
class FruitSalad {
val apple: AppleVariety
val banana: BananaVariety
val cherries: CheryyVariety
}
```
`FruitSalad` の値は `AppleVariety` x `BananaVariety` x `CheryyVariety` の値の組み合わせによって定まるので `AND Types` (直積) と呼ぶ
F#
```
// F# だけど Union はやっぱ `|` で書いた方がわかりやすいので F# の例も載せておく
// 多分ニュアンスは、単一のフルーツでできたおかし (ドライフルーツとか) のイメージなのかな
type FruitSnack =
| Apple of AppleVariety
| Banana of BananaVariety
| Cherries of CheryyVariety
```
Kotlin
```
// Kotlin で書くならこういう感じ?
sealed class FruitSnack
class AppleVarietyFruitSnack : FruitSnack
class BananaVarietyFruitSnack : FruitSnack
class CheryyVarietyFruitSnack : FruitSnack
```
いずれにせよ `FruitSnack` の値は `AppleVariety` + `BananaVariety` + `CheryyVariety` の値の組み合わせ分しかない (要するに直行するので組み合わせとならない) ので `OR Types` (直和) と呼ぶ
また上記の他に単一 (別に単一ではなくても意味のまとまりがあるならばそれをまとめてもいいと思う) の値を型でラップしたものもある
e.g.
Kotlin
```
data class CustomerId(val id: Int)
```
### Integrity (有効性)
Kotlin
```
data class WidgetCode(val code: String) // starting with "W" then 4 digits
data class UnitQuantity(val quantity: Int) // between 1 and 1000
data class KilogramQuantity(val quantity: Float) // between 0.05 and 100.00
```
上記のように多くのアプリケーション上で扱う概念が `String` や `Int` そのままの制約とあっているわけではない。
それらをコメントに書いてとりあえず他の開発者に伝えるという策も有効だが、そもそもインスタンス化できないようにしてしまうという手もある
Kotlin
```
class UnitQuantity private constructor(
val quantity: Int
) {
companion object {
fun create(qty: Int): UnitQuantity {
require(qty >= 1) { "UnitQuantity can not negative" }
require(qty <= 1000) { "UnitQuantity can not be more than 1000" }
return UnitQuantity(qty)
}
}
}
```
ちなみに自分はこのくらいならそもそも `init` ブロックの中に `require` を書くのがいいのではと思ってしまう
## 本題
ここまででおおよそテクニック的な話は終わりでここからが本題
### 状態
例えば、メールアドレスで認証済みかどうかという状態があったとしたときに
Kotlin
```
class CustomerEmail(
val emailAddress: EmailAddress
val isVerified: Boolean
)
```
上記のようにフラグを持たせるというのは一般的な解決方法の1つではあるが、この場合にはフラグを変え忘れたりしてしまうし例えば加えて認証済みのメールアドレスしかメールを送ってはいけない (その逆もしかり) などの実装がある場合に `isVerified` でフィルタリングをするのを忘れてしまうなどがあり得る。 (要するに気をつければいいわけだが、気を付けるにもコンパイラなどの恩恵を受けれないので開発者が気を付ける以外に方法がないというのが主張だと思う)
そもそもこういった2状態あるものについては、以下のように `OR Types` を使ってモデリングしてみようというのが提案
F#
```
// 毎度のことだけど Union は `|` の方が直感的に読みやすいので F# の例も書いておく
type CustomerEmail =
| Unveriied of EmailAddress
| Verified of EmailAddress
```
Kotlin
```
sealed class CustomerEmail {
abstract val address: EmailAddress
}
class UnveriiedCustomerEmail(override val address: EmailAddress): CustomerEmail()
class VerifiedCustomerEmail(override val address: EmailAddress): CustomerEmail()
```
このような実装をすることで、例えば「パスワードの再設定メールは認証済みのメールにしか送信してはいけない」という要求があるときに以下のような制約をかけたりすることができる
Kotlin
```
fun sendPasswordResetEmail(emailAddress: VerifiedCustomerEmail) {
// do something
}
```
またもう1つの例として「ある連絡先情報は、メールアドレスもしくは住所が必須となる」というビジネス要求を考えてみる
F#
```
// メールアドレスと住所が必須になってしまう
type Contact = {
Name: Name
Email: EmailContactInfo
Address: PostalContactInfo
}
// メールアドレスと住所どちらも設定しないことができてしまう
type Contact = {
Name: Name
Email: EmailContactInfo option
Address: PostalContactInfo option
}
// メールのみ、住所のみ、どちらも設定されているの3パターンを表現するために直和を利用する
type Contact = {
Name: Name
ContactInfo: ContactInfo
}
type ContactInfo =
| EmailOnly of EmailContactInfo
| AddrOnly of PostalContactInfo
| EmailAndAddr of BothContactMethods
type BothContactMethods = {
Email: EmailContactInfo
Address: PostalContactInfo
}
```
### 状態遷移 (state machine)
例えば以下のような簡易的なECサイトにおける買い物カゴの状態と代表的な状態遷移関数を書いてみる
F#
```
type Item = ...
type ActiveCartData = { UnpaidItems: Item list }
type PaidCartData = { PaidItems: Item list; Payment: float }
type ShoppingCart =
| EmptyCart // no data
| ActiveCart of ActiveCartData
| PaidCart of PaidCartData
// 買い物カゴに商品を追加する
let addItem cart item =
match cart with
| EmptyCart ->
ActiveCart { UnpaidItems=[item] }
| ActiveCart { UnpaidItems=exsitingItems } ->
ActiveCart { UnpaidItems=item::exsitingItems }
| PaidCart _ ->
cart // ignore
// 決済する
let makePayment cart payment =
match cart with
| EmptyCart ->
cart // ignore
| ActiveCart { UnpaidItems=exsitingItems } ->
PaidCart { PaidItems=exsitingItems; Payment=payment }
| PaidCart
cart // ignore
```
こうすることで以下のような状態遷移図がコードによって明確に表現できるためコードがビジネス要求を示すようになる
TBD 状態遷移図
## 参考情報
自分の登壇でもこの話を扱ったりしてた
[https://speakerdeck.com/omuomugin/designing_with_types_in_kotlin](https://speakerdeck.com/omuomugin/designing_with_types_in_kotlin)
こちらの動画でも話題になっていて、過程のイメージがしやすかった
[https://www.youtube.com/watch?v=lDMR-tqDRVs](https://www.youtube.com/watch?v=lDMR-tqDRVs)
# 感想