Upgrade to Pro — share decks privately, control downloads, hide ads and more …

関数型ドメインモデリングの理想と現実 / Domain Modeling Made Funct...

Sponsored · Ship Features Fearlessly Turn features on and off without deploys. Used by thousands of Ruby developers. →

関数型ドメインモデリングの理想と現実 / Domain Modeling Made Functional: Ideals vs. Reality

登壇者名:佐藤拓人、識名朝彬
登壇したイベントタイトル:TypeScriptでの関数型モデリング、どう実践する?導入のバランスと悩みを語り合う
登壇したイベントのURL:https://herp-inc.connpass.com/event/405705/

More Decks by 株式会社ビットキー / Bitkey Inc.

Other Decks in Technology

Transcript

  1. 目次 Manager / EM 1-1. なぜ関数型ドメインモデリング? 1-2. 関数型に固執した失敗と学び 1-3. Event

    Sourcing のアプローチでの解決 2-1. 入居時オンボーディングとは 2-2. 難しいポイント 2-3. 良 ったこと Member 2-4. 大変だったこと 2-5. まとめ 2 Copyright © 2025 Bitkey Inc. All right reserved.
  2. 目次 1-1. なぜ関数型ドメインモデリング? 1-2. 関数型に固執した失敗と学び 1-3. Event Sourcing のアプローチでの解決 2-1.

    入居時オンボーディングとは 2-2. 難しいポイント 2-3. 良 ったこと 2-4. 大変だったこと 2-5. まとめ 3 Copyright © 2025 Bitkey Inc. All right reserved.
  3. 自己紹介 佐藤 拓人 Sato Takuto @takuuuuuuu777 2015.04 大学(建築学専攻)卒業後、 株式会社ワークスアプリケーションズに入社 会計システムのソフトウェア開発を担当

    特に財務会計の仕訳関連 2019.05 ビットキーへ参画 ECサイトの開発 / 保守、社内システムの開発 TaKuTyの開発 2020.01 今のhomehub事業の前身となるResidenceチームに配 属。スマートロックを扱う管理画面やバックエンド、 スマホアプリの開発に従事 Now homehub事業のプロダクト責任者 複雑な事象を読み解いて構造化し、抽象化 / 汎用化で るように設計し、低コストで多 の価値をだせる開 発をすることを好む 4 Copyright © 2025 Bitkey Inc. All right reserved.
  4. 1. なぜ関数型ドメインモデリング? 背景 / 課題 ・過去にスパゲッティ化 加速されてしまった状態の改善のために「DDD」を導入していた そもそもモデリングが 難しい... ・当初は「ドメイン駆動」より、機能追加や修正時に「壊れづらい構造」を重要視して開発をしていてた

    ・ある程度「DDD」の構造 進んで課題となったのは「モデリングの共通認識」 ・境界づけられてたコンテキストなどの理解や業務理解などのために、イベントストーミングなどを 実施した 解決し れな った...。 8 Copyright © 2025 Bitkey Inc. All right reserved.
  5. 1. なぜ関数型ドメインモデリング? ▪ モデリングとは? - 現実を模倣して単純化したもの 単純化のために、情報を取捨選択して削 落とす ▪ 目的・メリット

    - 単純化するため、実装しやすくなる 単純化するため、変更しやすくなる 単純化した結果共通項が明確になり共通化しやすくなる ▪ 例:地図 9 Copyright © 2025 Bitkey Inc. All right reserved.
  6. 1. なぜ関数型ドメインモデリング? ▪ モデリングとは? - 現実を模倣して単純化したもの 単純化のために、情報を取捨選択して削 ▪ 目的・メリット -

    落とす これが難しい 単純化するため、実装しやすくなる 単純化するため、変更しやすくなる 単純化した結果共通項が明確になり共通化しやすくなる ▪ 例:地図 10 Copyright © 2025 Bitkey Inc. All right reserved.
  7. 1. なぜ関数型ドメインモデリング? モデリングの難しさ 1. そもそも抽象化 難しい 2. 粒度や取捨選択 難しい 3.

    これを局所最適ではな 4. 「創造」の側面 強 全体最適とするの 、これ 難しい 良いという共通認識を取りづらい 11 Copyright © 2025 Bitkey Inc. All right reserved.
  8. 1. なぜ関数型ドメインモデリング? 失敗 ・取り入れたい要素を見誤ってしまった... 補足 ・元々の課題はモデリング ・関数型という具体的な実装方法に固執する必要 ・実装方法ではな ・にも な

    った 、モデリングの考え方 / アプローチに取り入れた った わらず、実装方法も関数型に限定して固執してしまった 学び ・Event Sourcing でモデリングに対する関数型アプローチを実現可能だった 16 Copyright © 2025 Bitkey Inc. All right reserved.
  9. 1. なぜ Event Sourcing を導入しようと思ったの プロダクト特性 解錠できるか / できないが コントロールできない

    ... ↓ 事業リスク ※ かなり簡易化した図となっています 24 Copyright © 2025 Bitkey Inc. All right reserved.
  10. 2. なぜ Event Sourcing 「良い」と思ったの ? State管理だと難しい? 例え ...設置状況として 「処理開始」「処理中」「処理完了」

    …と管理すれ できる ... ず! (雑すぎるかもですが ...) 25 Copyright © 2025 Bitkey Inc. All right reserved.
  11. 2. なぜ Event Sourcing 「良い」と思ったの ? State管理だと難しい? 「処理中」がどんな状態を 意味するかが実装に 依存してしまう

    でも... 設置状況: 「処理中」 そもそも「設置状況」と 何? そ うち処理 (実装)が …ってどんな状況? 変わるかもしれない ... ステータスと乖離するかも ... 27 Copyright © 2025 Bitkey Inc. All right reserved.
  12. 2. なぜ Event Sourcing 「良い」と思ったの State管理だと難しい? 「状態」 ? 「処理状況」がどんな状態を 表現するかがソースに

    依存してしまう 現実に 存在しない「概念」 設置状況: 「処理中」 そ ため移ろいやく …ってどんな状況? そもそも「設置状況」とそして壊れやすい 何? そ うち必要な処理 (実装)が 変わるかもしれない ... ステータスと乖離するかも ... 28 Copyright © 2025 Bitkey Inc. All right reserved.
  13. 2. なぜ Event Sourcing 「良い」と思ったの ? State管理だと難しい? State管理 課題 (個人的な課題

    ) 1. 人によって認識 仕方が異なる 2. 事業 フェーズやドメイン理解が深まることによって、あるべき定義が変わ りやすい 3. 状態 …上記 根拠が「ソースコード」になり、変更されやすい 特性がある「状態」を一次情報として管理する リスクが高い ... 29 Copyright © 2025 Bitkey Inc. All right reserved.
  14. 2. なぜ Event Sourcing 「良い」と思ったの ? State管理だと難しい? State管理 課題 (個人的な課題

    ) 1. 人によって認識 仕方が異なる つまり、むずい 2. 事業 フェーズやドメイン理解が深まることによって、あるべき定義が変わ りやすい 3. 状態 …上記 根拠が「ソースコード」になり、変更されやすい 特性がある「状態」を一次情報として管理する リスクが高い ... 30 Copyright © 2025 Bitkey Inc. All right reserved.
  15. 1. なぜ Event Sourcing を導入しようと思ったの Event Sourcing カード登録 コマンドを実行した ※

    かなり簡易化した図となっています 32 Copyright © 2025 Bitkey Inc. All right reserved.
  16. 1. なぜ Event Sourcing を導入しようと思ったの Event Sourcing カード登録 コマンドを実行した カード登録

    コマンドが成功した ※ かなり簡易化した図となっています 33 Copyright © 2025 Bitkey Inc. All right reserved.
  17. 1. なぜ Event Sourcing を導入しようと思ったの Event Sourcing カード登録 コマンドを実行した カード登録

    コマンドが成功した カード登録 コマンドが失敗した ※ かなり簡易化した図となっています 34 Copyright © 2025 Bitkey Inc. All right reserved.
  18. 2. なぜ Event Sourcing 「良い」と思ったの ? 客観的に観測可能な カード登録 コマンドを実行した カード登録

    コマンドが成功した カード登録 コマンドが失敗した 具体的な「イベント」 羅列や整理から モデリングを進めることができる 35 Copyright © 2025 Bitkey Inc. All right reserved.
  19. 2. なぜ Event Sourcing 「良い」と思ったの ? 客観的に観測可能な カード登録 コマンドを実行した 具体的な「イベント」

    これが良かった! カード登録 コマンドが成功した カード登録 コマンドが失敗した 羅列や整理から モデリングを進めることができる 36 Copyright © 2025 Bitkey Inc. All right reserved.
  20. ⾃⼰紹介 2020.04 新卒Webエンジニアとしてキャリアスタート! ⼩中学校で使われるクラウド型学習サービスを つくっていた 識名 朝彬(なっち) Tomoaki Shikina 2

    2023.06 ビットキーへ参画 Now homehubの中核となる、デバイスとつながる部分やス Hobby homehubの賃貸領域の主にバックエンド開発に従事 マートアクセスの部分にも領域を広げ、⽇々精進中 サウナ 王⼦近辺のサウナによく⾏きます 童話式振り返り ⾝の回りで起こった出来事を童話にして振り返り、 学びを得ています Copyright © 2025 Bitkey Inc. All rights reserved.
  21. 目次 1-1. なぜ関数型ドメインモデリング? 1-2. 関数型に固執した失敗と学び 1-3. Event Sourcing のアプローチでの解決 2-1.

    入居時オンボーディングとは 2-2. 難しいポイント 2-3. 良かったこと 2-4. 大変だったこと 2-5. まとめ 39 Copyright © 2025 Bitkey Inc. All right reserved.
  22. 1. 入居時オンボーディングとは 入居時オンボーディング = 部屋に入居すると の最初の準備 01 02 03 前の入居者の情報が

    管理用の設定を最新に スマートロックの鍵穴 残っていたら消す する を交換する 物理鍵に ける「シリン ダー交換」に相当 40 Copyright © 2025 Bitkey Inc. All right reserved.
  23. 2. 難しいポイント デバイスに対する通信を介する • 環境によって一発で成功するとは限らな い • 時間 「あけしめ」に関わることなので… •

    必要に応じて、入居者や管理会社にアク ったりもする 入居者がアプリから操作する • 途中でタスクキルされたり... ションを促したい • 問題 生じていそうなら、検知して対応し たいしたい • 失敗してもそのまま放置したり... 41 Copyright © 2025 Bitkey Inc. All right reserved.
  24. 良 ったこと / 大変だったこと 良かったこと ・ドメイン整理の議論 しやすい / 変更時も開発者間での認識 合わせやすい

    ・イベントソーシングにより実装も自然と関数型に寄り、嬉しさを享受で た 大変だったこと ・イベントと状態遷移の数 膨らみ、実装も複雑に... ・ステートの表現にTypescriptのクラスを採用したことによるつらみ ・Result型で、どういうエラー 発生しうる の洞察は深まった ... 43 Copyright © 2025 Bitkey Inc. All right reserved.
  25. 良 ったこと / 大変だったこと 良かったこと ・ドメイン整理の議論 しやすい / 変更時も開発者間での認識 合わせやすい

    ・イベントソーシングにより実装も自然と関数型に寄り、嬉しさを享受で た 大変だったこと ・イベントと状態遷移の数 膨らみ、実装も複雑に... ・ステートの表現にTypescriptのクラスを採用したことによるつらみ ・Result型で、どういうエラー 発生しうる の洞察は深まった ... 44 Copyright © 2025 Bitkey Inc. All right reserved.
  26. 4. 良 ったこと ① ドメイン整理の議論 しやすい / 変更時も開発者間での認識 合わせやすい 「何が起きるんだっけ?」をベースに議論できた

    • 『どういう状態 ありうる 』は抽象度 高 、発散 しやすい。 • 一方で、『何 起 るんだっけ?』は具体的な出来事 を列挙するため、開発者間やビジネスサイドとも認識 を合わせやすい 例: 入居時オンボーディングでの整理 • 起こることの列挙と、その時どういう状態になる • 上記に対する対応を機能としてどう実現する ? ? を整理したことで、ビジネスサイドとも認識齟齬少な 整理を進められた 45 Copyright © 2025 Bitkey Inc. All right reserved.
  27. 4. 良 ったこと ① ドメイン整理の議論 しやすい / 変更時も開発者間での認識 合わせやすい 「何が起きるんだっけ?」をベースに議論できた

    • 『どういう状態 ありうる 』は抽象度 高 、発散 しやすい。 • 一方で、『何 起 るんだっけ?』は具体的な出来事 を列挙するため、開発者間やビジネスサイドとも認識 を合わせやすい 例: 入居時オンボーディングでの整理 • 起こることの列挙と、その時どういう状態になる • 上記に対する対応を機能としてどう実現する ? ? を整理したことで、ビジネスサイドとも認識齟齬少な 整理を進められた 46 ※表現は一部変えてます Copyright © 2025 Bitkey Inc. All right reserved.
  28. 4. 良 ったこと ① ドメイン整理の議論 しやすい / 変更時も開発者間での認識 合わせやすい 変更する時も同じ

    • 「どういう出来事」がどこに新たに追加される か?を中 心に議論でき、認識ずれなく整理、実装を行えた 例: 新デバイスへの対応 • 親機と子機があり、それらが連携して動作するようなデバ イスへ 対応 起こりうることとそれによる状態遷移を定義していた で、そ れに対してどこに何を追加するか?そ 結果どうなるか?を 認識合わせつつ進められた 47 Copyright © 2025 Bitkey Inc. All right reserved.
  29. 4. 良 ったこと ② イベントソーシングにより実装も自然と関数型に寄り、嬉しさを享受で 「起こったこと」を軸に、遷移関数を実装 • ある状態 ら次の状態に遷移する関数を、イベン トごとに1つ

    • テストしやす 、レビューの認知負荷も下げられ た 型で、状態ごとの「情報」と「操作」を定義 • 型のガードレールやエディタの補助を受けつつ開 た 状態 持ちうる情報 受け付けられる操作 要求済み 要求日時 成功した/失敗した 失敗 失敗理由 再試行する 完了 完了日時 (なし) 「失敗」状態だけ ほ 失敗理由を持ち、「再試行」を受け付ける。 の状態で再試行を呼ぼうとすると、コンパイル 通らない 発で た 当時はまだエディタを使った開発 メインだった ので非常に助けられた 48 Copyright © 2025 Bitkey Inc. All right reserved.
  30. 良 ったこと / 大変だったこと 良かったこと ・ドメイン整理の議論 しやすい / 変更時も開発者間での認識 合わせやすい

    ・イベントソーシングにより実装も自然と関数型に寄り、嬉しさを享受で た 大変だったこと ・イベントと状態遷移の数 膨らみ、実装も複雑に... ・ステートの表現にTypescriptのクラスを採用したことによるつらみ ・Result型で、どういうエラー 発生しうる の洞察は深まった ... 49 Copyright © 2025 Bitkey Inc. All right reserved.
  31. 5. 大変だったこと イベントと状態遷移の数 膨らみ、実装も複雑に… 出来事が増えると、状態と遷移も増える • 1つ1つの遷移関数は小さいままなので見やすい。 • し し、全体像は肥大化して実装では追いづら

    すべてを大きなステートマシン なる 中で扱わず より小さなサブセットに分割することで改善 できそう 例) 前の入居者の情報削除: イベント1, イベント2… 管理用の設定刷新: イベント3, イベント4… 50 Copyright © 2025 Bitkey Inc. All right reserved.
  32. 5. 大変だったこと ステートの表現に TypeScript のクラスを採用したことによるつらみ 判定メソッドを抽象クラスに置いた • state. と打てば、判定もその状態だけ 持つ操作も候補に出て

    る • 当時はエディタ中心の開発だったので、この体験を優先した 関数にすると名前をある程度覚えて import しないと辿れない... 問題点: 親と子が互いに型参照し、ファイル分割できない • 親は this is XxxState で子を参照し、子は extends で親を参照 する...(よ • ない) その結果状態と遷移 すべて1ファイルに。一つ一つの遷移ロ ジックは小さい ファイルは大 なり、全体像 掴みづらい // 親 -> 子:判定の戻り値の型で、子を参照 abstract class StateAbstract { isStarted = (): this is StartedState => ... isCompleted = (): this is CompletedState => ... } // 子 -> 親:extends で、親を参照 class StartedState extends StateAbstract { ... } class CompletedState extends StateAbstract { ... } keyExchange. // isKeyExchangeStarted() isKeyPairFullyUpdatedOnBkp() ... 判定が候補に出る if (keyExchange.isKeyPairFullyUpdatedOnBkp()) { keyExchange. // 型で絞られた候補だけ出てくる fullyUpdatedHoles getUpdatedOnBkpAndNotUpdatedOnDeviceHoles() } 51 Copyright © 2025 Bitkey Inc. All right reserved.
  33. 5. 大変だったこと Result型で、どういうエラー 発生しうる の洞察は深まった • 当初、欲しいのはただのOK / NGを表すバリアントだと 考えていたので、自作Result型を作成して対応

    • しかし当然ながら、合成が右 ... const loaded = await this.loadAndValidate(param); if (loaded.error) return ResultUtil.failure(loaded.error); const started = await this.startKeyExchangeIfNeed({...}); if (started.error) return ResultUtil.failure(started.error); ようにかなり冗長に... const targets = await this.detectRedeliverTargets({...}); if (targets.error) return ResultUtil.failure(targets.error); 自作Resultに必要に応じてandThenなどを用意しつつ、 重くなってきたらneverthrowなど ライブラリを導入する 良さそう const checked = await this.dep.checkDelivered.handle({...}); if (checked.error) return ResultUtil.failure(checked.error); が …… return ResultUtil.success(state); 52 Copyright © 2025 Bitkey Inc. All right reserved.