Grokking
Simplicity探訪
kawasima
アーキ部
Grokking Simplicity
2021年5月 発売
日本語未訳
他Grokkingシリーズは「なっとく!」シリーズと
して続々翻訳されているが…
Data, Action, Calculation
計算
アクション
データ
実行結果が、何回実行するか、いつ実行するかに依存する
入力から出力を計算する
イベントに関する事実
例) メールを送る、データベースから読み込む
例) 最大値を探す、メールアドレスの妥当性を検証する
例) 最大値を探す、メールアドレスの妥当性を検証する
プログラムを以下の3つに分類してみよう
Calcuration
『Grokking Simplicity』の定義
入力から出力を計算する関数
● 外の世界に影響を与えず、外の世界の影響も受けない
● 例
○ +, *, -, /
○ Math.abs()
○ 文字列結合
○ Eメールアドレスのバリデーション
Action
『Grokking Simplicity』の定義
外の世界に影響を与える、または外の世界の影響を受ける関数
したがって純関数でないもの、または副作用のあるもの
● 例:
○ Eメールを送信する
○ データベースから読み込む
○ ファイルに書き込む
● Actionは本番環境で安全に実行するのが難しい
● Actionはデバッグするのが難しい
Calculation 割合を増やしたい
Calculations Calculations
Actions Actions
こっちの方が安全でデバッグしやすい
https://speakerdeck.com/masuda220/big-ball-of-mud
このあたりの話は、増田さんのスライドでも書かれているので、
ぜひご覧になってください。
Action 波及ルール
Eric Normand『Stratified design and functional architecture』より
←これがActionだと…
Action 波及ルール
Eric Normand『Stratified design and functional architecture』より
←呼んでいる関数全体が
Actionになり…
Action 波及ルール
Eric Normand『Stratified design and functional architecture』より
←結果全てのコードがAction
になる
Action 波及ルールへ 抵抗
できる限りコールツリーのリーフにCalculationを持ってくる必要がある
←Actionを呼んでいるので
これもActionになってしまう!
Layered Architectureで どうか?
Web層
Application層
Domain層
Data Access層
リーフがData AccessかDomainかになる
Data AccessはActionなので、
CalculationなDomainを作らない限り、
全てがActionになってしまう。
Traditional Layered Architecture
そこまでして純粋性にこだわる必要 ある か?
https://www.slideshare.net/slideshow/ss-255489610/255489610
ないかもしれない。何を取りに行きたいのか? 要はバラ(略
終
と、これだけだとよくある話なのですが、
Calculation抽出を追う過程でより深い示唆がえられる
というのが今日の話です。
Grokking Simplicity
Deep Dive
Calculationを増すのを追い求めるのは、関数の呼び出し階層を作る行為でもある
階層?
Web層
Application層
Domain層
Data Access層
すでにレイヤ分けはしているのだが…?
何のためにレイヤを分けているのか?
そもそもレイヤードアーキテクチャと ?
● ソースコードの変更がシステム全体に波及しないようにする。変更は一つのコンポーネント
に閉じ込め、他の部分に影響を与えないようにする。
● インターフェースは安定しているべきである。
● システムの一部は交換可能であるべきである。コンポーネントは、システムの他の部分に影
響を与えることなく、代替実装に置き換えられるべきである。
● 似た責務を持つ要素をグループ化して理解しやすさと保守性を高めるべきである。各コン
ポーネントは一貫性を持つべきであり、一つのコンポーネントが異なる問題を実装すると、
その整合性が失われる可能性がある。グループ化と一貫性は時に対立する。
『Pattern-Oriented Software Architecture』
POSA Layered Architecture
Layer N
Layer N-1
Layer N-1
抽象度が高い
抽象度が低い
『Pattern-Oriented Software Architecture』
同じ抽象度のコンポーネントをグルーピングする
各レイヤのコンポーネントは、自分と同じか1つ
下のレイヤの機能のみに依存する。
個々のレイヤー内ではすべてのコンポーネントが
同じ抽象レベルで動作することが不可欠。
Traditional Layered Architecture レイヤ分割基準 ?
抽象度によって分かれている?
そもそも抽象とは何か?
Web層
Application層
Domain層
Data Access層
A, B, Cの共通項
A B C
全体
部分 部分 部分
これも含まれることがある
目的
手段 手段 手段
振る舞いにフォーカスすると…
抽象
抽象
抽象
目的-手段 関係 フラクタル
目的A
手段 手段 手段
手段
目的Aの手段
かつ目的B
目的Bの手段 目的Bの手段 手段
手段
目的Bの手段
Action, Calculation コールツリーも
目的-手段 フラクタルで構成されれ …
目的
手段 目的
手段 目的
手段
抽象のレイヤーができる
Stratified Design
https://medium.com/clean-code-development/stratified-design-over-layered-design-125727c7e15
さらに、コールツリーを詳細の実装を持
つのはリーフのみにする。ブランチノー
ドは、下位層をコールするのみ。
詳細の実装は他のものに依存しないの
で、テストがしやすい(テストダブルの必
要がない)。
他のものに依存しないので、だいたい
Calculationになる。
これは別に新しい考え方とい
うわけでもなく…
Composed Method
メソッドを他のメソッドの呼び出しから構成し、それぞれがほぼ同じ抽象度の
レベルにあるようにする。
※ ただしあまり具体的なことは『実装パターン』には書いてはいない
ケントベックならそうする
Stratified Designによってもたらされるも
Layered Architectureの本来の目的の1つ
● 似た責務を持つ要素をグループ化して理解しやすさと保守性を高めるべき
例題
ナントカPayを作ります。
● ウォレットにコインをチャージして、利用したり送金したりできます。
● 銀行口座を登録しておくと、そこからウォレットにチャージできます。これ
は必須ではありません。
● 銀行口座を登録する場合は、本人確認が必要です。本人確認は免許証または
マイナンバーで行います(外部API利用)
● 口座番号が実在するか確認する必要があります。(外部API利用)
● メールアドレスがブラックリストに載っていれば登録できません。(ブラック
リストはテキストファイルに1行ずつNGメールアドレスが記載されている)
● メールアドレスは全ユーザで一意でなくてはなりません。
● これまで一度も登録したことのないユーザであれば、ウォレットに500コイン
を付与します。
https://bitbucket.org/kawasima/design-kata/src/main/src/example/06_pay/
StratifiedでないDesgin
ユーザを登録する
運転免許証を使っ
た本人確認
銀行口座 実在
確認
マイナンバーを
使った本人確認
csvから1行読
み出す
Eメールが他
ユーザと一致して
いないか
ユーザをDBに
保存する
初回登録かどうか
を判定する
ウォレットをDB
に保存する
どれがドメインロジック ?
https://bitbucket.org/kawasima/design-kata/src/main/src/example/06_pay/register.ts
StratifiedなDesign
ユーザを登録する
運転免許証を
使った本人確認
銀行口座 実在
確認
マイナンバーを
使った本人確認
csvから1行読
み出す
ユーザをDBに保
存する
初回登録
リワード
本人確認する
銀行口座 妥当
性を確認する
メールアドレス 妥
当性チェック
Eメールが他
ユーザと一致し
ていないか
Eメールによる
ユーザ検索
ブラックリストにメ
アドが載っていな
い
初回登録か判定
初回登録検索
初回登録コイン
付与
←大まかな業務ルール
↓詳細の業務ルール
https://bitbucket.org/kawasima/design-kata/src/main/src/example/06_pay/register.ts
保守性↑
ユーザを登録する
運転免許証を
使った本人確認
マイナンバーを
使った本人確認
本人確認する
ユーザを登録する
運転免許証を
使った本人確認
マイナンバーを
使った本人確認
本人確認する
年金手帳を使っ
た本人確認
← 変更なし
本人確認の手段が増えても、
ユーザを登録する部分は変更しなくても良い
↑追加
理解しやすさ↑
Stratified
Non-Stratified
汎化
抽象化することを汎化(Generalization)するということもあるようだが…
抽象化すると汎用的になる、コードの再利用性を高めるってこと?
「汎用」 2つ 意味がある
広範に使われる
広範を扱える
異なる概念
なので前ページのGPT-4oの回答には異を唱えたい
「広範を扱える」
上位は下位の目的になってお
り、下位は上位の手段になっ
ている。
目的
手段
抽象
具象
汎化
特化
柔軟性や理解容易性に関わってくる
いわゆる抽象化の概念
「広範に使われる」
ある手段が複数の目的で使われる。
再利用性に関わってくる
「汎用的なものを作ろう!」と言った場合、
どちらを指しているかを認識することが重要
「広範を扱え」で「広範に使われる」構造
「広範を扱える」と「広範に使われる」が両立するセミラティス構造
それらは両立可能で、両立するとこのような構造になるはず。
←リーフがCalculationになっている
とモアベター
←同じ抽象レベルのコンポーネント
Bad Scenario
1. レイヤごとにコンポーネントを分割し、その責務を考える。
2. 柔軟性を持たせるために汎化できるものを考える (部分的抽象化)
3. 汎化した処理は、よりバリエーションの多いデータを受け付ける。
そのため処理に重複するところが出てくる。
4. 重複するところを共通関数(下請けメソッド)として切り出す。(部分的再利用性)
レイヤごとに責務を割り当てる設計からスタート
する術しか持っていない場合…
よく見かけるヤツ!
Stratified Design 階層 特徴
目的
手段
抽象
具象
特定用途
汎用
Clean Architecture
“ソースコードの依存関係は常に内側を指す。内側に進むに
つれて、抽象度と方針のレベルが高まる。最も外側の円は
低レベルの具体的な詳細で構成されている。内側に進むに
つれて、ソフトウェアはより抽象的になり、高レベルの方
針を包含する。最も内側の円が最も一般的で高いレベルで
ある。”
円 中心ほど抽象度が高い?
円 中心ほど抽象度が高いと言って
いるが、これが決定的な誤り。
ユースケースが目的で、ビジネス
ルール そ 手段な で、ユース
ケース 方がハイレベル、抽象度が
高い、と考える が普通で ?
Controller
Repository
UseCase
Presenter
Business
Rules
Repository
Business
Rules
Repository
Impl
Presenter
Impl
もしかしたらDIPによる依存 逆転が
ある で、最下層が一番抽象的と
言っている かもしれない …
…が、Business Rules UseCase 手
段であり、ここに関して POSA レ
イヤリング 定義にピタッと当て ま
る で、UseCase 方が抽象度が高
いだろう…
そもそも実装分離 ため インタフェース 抽象と言える だろうか?
Repository
RepositoryImpl
抽象化と …?
“抽象化とは物事を曖昧にすることではなく、 むしろ
人が絶対的な正確さを発揮できる新しい意味の段階を
つくりだすこと”
Edsger W. Dijkstra
実装を切り離す目的 インタフェースを抽象化と呼 ない方が良いかも
Repository
RepositoryImpl
目的と手段の関係にはなっていないし、
新しい意味の段階を作ってはいない
Client
使う側はどちらにインタフェースと実装とどちらにアクセスしても、
その意味は変わらない。
クリーンアーキテクチャ DIP 新しい意味 段階を生み出しているわけで ない で取り除いてみると…
Controller
Repository
UseCase
Presenter
Business
Rules
伝統的なレイヤードアーキテクチャと
なんら変わらない。
Repository
Business
Rules
← 大事なのはこのレイヤのStratified Design
業務ルール 抽象化
同じ振る舞いをグルーピングするだけで
なく、大まかに言えば同じ振る舞いと見
なせる新しい意味の段階を意図的に作る
ユーザを登録する
運転免許証を
使った本人確認
銀行口座 実在
確認
マイナンバーを
使った本人確認
csvから1行読
み出す
ユーザをDBに保
存する
初回登録
リワード
本人確認する
銀行口座 妥当
性を確認する
メールアドレス 妥
当性チェック
Eメールが他
ユーザと一致し
ていないか
Eメールによる
ユーザ検索
ブラックリストにメ
アドが載っていな
い
初回登録か判定
初回登録検索
初回登録コイン
付与
←大まかな業務ルール
↓詳細の業務ルール
業務ルール 抽象化
● 業務ルールを抽象化しておくと、ルールの追加・変更が下位層に閉じ
柔軟性が増す。一度に脳にロードしておくべきコンポーネントが減
る、認知負荷が減るので、理解容易性が増す。
→ 「広範を扱える」ようにするメリット
● 業務ルールを抽象化しておくと、下位層のルールの再利用性が増す。
→ 「広範に使える」ようにするメリット
○ ルール自体をコピペするのではなく、詳細ルールを合成して、
新しいルールを作ることもできる。
で Stratified Design当たり前 こと ような に、なぜメジャーじゃない か…?
https://ja.wikipedia.org/wiki/凝集度
抽象概念 設計が難しく、ミスると凝集度を下げてしまうため
誤って凝集度を下げることなく、Stratified
なDesignをするやり方については、
7/13 の大吉祥寺.pmでお話する予定です!
お楽しみに!

Grokking Simplicity探訪