ロジックから状態を
分離する技術
設計ナイト2024
わいとん @ytnobody
私について
• 東 聡志(AZUMA Satoshi)
• a.k.a. @ytnobody – わいとん
• 零細システム開発会社経営
• Y.pm LLC
• バックエンドエンジニア25年生
• YAPC::Hakodate 2024実行委員
みんな、盛り上げていこうぜ!
話すこと
なぜ純粋関数で作るのか
やらないこと
副作用を取り扱う層と純粋関数だけの層を分離する
テストがしやすくなる以外のメリット
純粋関数デザインパターン
なぜ純粋関数でつくるのか
ロジックから状態を分離する技術
そもそも「純粋関数」とは
何なのか
• e-words.jpによると…
> 純粋関数(pure function)とは、プログラミングにお
ける関数のうち、同じ入力に対して常に同じ出力を返し、
副作用が生じないもの。
わかったようなわからんような…🤔
もうちょっと言い換えてみよう😣
純粋関数であるための条件
同じ引数に対し、決まった返り値を返す
引数以外の入力を持たない
返り値以外の出力を持たない
関数内で乱数生成を行わない
多少イメージできたかも😌
純粋関数における3つの「容易」
テストが
容易
x + 15 = y; x = 22; のとき、yは37であると
確信できるのと同じ理屈
流用が
容易
状態をもたないからこそ可搬性・再利用性が
極めて高い
分離が
容易
フレームワークやORMと結合されておらず、
流行り廃りとは無関係
純粋関数でつくるのは
あらゆる面で「容易」を求めるため
• テスト・流用・分離が容易であれば読み手にとっても「容易」
となりやすい。
• 長くメンテナンスすることを容易にする
• 不具合の発見を容易にする
• 引継ぎ・教育を容易にする
• 改造を容易にする
• 「選ばれし者」だけがコミットできる仕組みは長続きしづらく、
引継ぎや改造が大変で、不具合調査が難航しがち。
• だから徹底的に「容易」を取りに行くし、それが純粋関数でつ
くる理由となる。
やらないこと
ロジックから状態を分離する技術
メンバ変数とカプセル化
• instance = new MyClass(some_attributes)
• MyClassはsome_attributesに依存するかしないかわからないが、
内部に状態を持っているかもしれないし、持っていないかもしれない。
• decoratorやannotation等で識別可能するというアプローチは
かえって複雑性を増すのでNG。
• インスタンス内部の状態を予測・忖度しながらコードを書くの
は難しい
• インスタンス内部に値を持たなければカプセル化の存在意義は
なくなる
データとロジックの混在
• もっと言うとRowクラス
• 型としてのRow型は良い
• データだけ持つ分にはOK
• Rowクラスにメソッドを生やすと、どのRowクラスにどのメソッドが
あるのかを意識するコストが発生する
• これは、次に挙げる「継承」の最大要因となる
継承
• 継承元のロジックを受け継ぐことができることの功罪
• ロジックの前後関係が分断される
• 関数本体に書かれていない処理が実行される
• 継承元の仕様を知らないとコードを書けないし読めない
• 多世代継承が発生すると、どの世代がどの処理を行っているの
かわからなくなる
• このあたりは継承の利用ルールで予防できる
副作用を取り扱う層と
純粋関数だけの層を
分離する
ロジックから状態を分離する技術
例:MVCにおけるFat Model問題
Model
- DBとのやり取り
- ビジネスロジック実装
- ファイルハンドリング
- クラウドリソース取扱
- 各種状態の管理
- 外部システムとの連携
View
- テンプレートに基づくデータ整形
Controller
- 要求の受付
- Modelへの処理要求
- Viewへの描画要求
- 応答の返却
Fat Modelの根本的な病理
• Modelに期待される役割が多すぎる
Fat Model問題を解くために
MVCを崩してみる(Modelを分解)
Service
- ビジネスロジック実装
- 各種状態の管理
- Infraとのやり取り
View
- テンプレートに基づくデータ整形
Controller
- 要求の受付
- Serviceへの処理要求
- Viewへの描画要求
- 応答の返却
Infra
- DBとのやり取り
- ファイルハンドリング
- クラウドリソース取扱
- 外部システムとの連携
コード量が多くなりがちな役割は何か
• 多くの場合「ビジネスロジック」と「各種状態の管理」が
分厚くなる傾向
分厚くなりがちな役割を分ける
Usecase
- ビジネスロジック実装
- Serviceとのやり取り
View
- テンプレートに基づくデータ整形
Controller
- 要求の受付
- Usecaseへの処理要求
- Viewへの描画要求
- 応答の返却
Infra
- DBとのやり取り
- ファイルハンドリング
- クラウドリソース取扱
- 外部システムとの連携
Service
- 各種状態の管理
- Infraとのやり取り
循環参照を排除するには
• コンポーネント間の参照方向を厳格に一方通行とする
参照方向を厳格に一方通行にする
Usecase
- ビジネスロジック
実装
View
- テンプレートに
基づくデータ整形
Controller
- 要求の受付
- 応答の返却
Infra
- システム外部との
やり取り
Service
- 各種状態の管理
ようやく本題にたどりつけました😇
ビジネスロジックから純粋関数を
分離する
• データ整形処理を純粋関数化
• 構造体に値を詰め込む処理
• 文字列結合・テンプレート処理
• フォーマット処理
• 純全たる計算処理
• 条件が複数の判断条件を純粋関数化
• if(user.role === ADMIN && user.isBanned === false) みたいなやつ
• 上記をここでは「狭義のビジネスロジック」と呼ぶ
• 狭義のビジネスロジックをDomainにまとめる
• DomainはUsecaseおよびServiceから参照される
UsecaseやServiceにある純粋関数候補を
Domainとして切り出す
Usecase
- ビジネスロジック
View
- テンプレートに
基づくデータ整形
Controller
- 要求の受付
- 応答の返却
Infra
- システム外部との
やり取り
Service
- 各種状態の管理
Domain
- データ整形(純粋関数)
- 判断条件(純粋関数)
よく聞く反駁
• ビジネスロジックと状態は切っても切り離せない
広義のビジネスロジックとしては
その通り
• 狭義のビジネスロジックを前提に考えると
「状態は引数として渡されるべき」である。
テストがしやすくなる以外の
メリット
ロジックから状態を分離する技術
ロジックの流用がしやすい
• 純粋関数として実装されたビジネスロジックは、その利用のた
めの前提条件が「引数の型が期待通りである」だけである。
• テストが書きやすい故に品質が高められやすく、ロジック流用
をするうえで求められる品質に達しやすい。
ORMやフレームワークから独立している
• ORMやフレームワークとは全く独立しているので、比較的容易
に別のORMやフレームワークに乗り換えができる。
• ORMやフレームワークよりも長寿命のコードをつくることができる。
状態に関する考慮を軽減できる
• 状態そのものを引数として受け取るため、テストケースとして
記述できる。
• そのため、考え得るあらゆる状態を網羅したテストを書くこと
ができ、特にDBに格納されたデータに基づく処理について安
全性を高めることが容易。
純粋関数デザインパターン
ロジックから状態を分離する技術
純粋関数にもデザインパターンが
ほしい!
• 勝手にいくつか列挙してみました。
Conditional Function – 条件関数
• if文の中で利用できるように作られた関数。
• 返り値はBool値のみ。
• 接頭辞にisやhas等が使われる
• 複雑な組み合わせ条件を条件関数にまとめると明快である。
条件関数の例:isSweetFruit
Switching Function – スイッチ関数
• 条件関数をより高度にしたもの。
• 引数が合致する条件に応じ、所定のenum値を返す。
スイッチ関数の例:getTypeOfFood
Template Function – テンプレート関数
• 入力値を元に文字列を組み立てる関数。
• URLを組み立てたり、文章を作ったりさせるのに使います。
テンプレート関数の例:
buildFoodDescription
Data Construction Function –
データ構築関数
• 入力値を元に構造体を組み立てる関数。
• レスポンス型にデータを当て込むなどの様々なデータ形式の構
築に使います。
データ構築関数の例:buildFoodResponse
まとめ
ロジックから状態を分離する技術
まとめ
• 純粋関数でつくるのは、あらゆる「容易」を取りに行くため。
• そのために以下の事を守っていく。
• 状態を持たせない作りと継承を最小限に抑える
• 分厚くなりがちなコンポーネントの責務を分割していく
• コンポーネント間の参照方向を一方通行にする
• 狭義のビジネスロジックとして純粋関数を仕上げていく
• 状態を引数として渡す
• そうして得られるメリットは、システムの健康寿命を
大きく伸ばすことに貢献する。
おわり
ありがとうございました

ロジックから状態を分離する技術/設計ナイト2024 by わいとん @ytnobody