UMTPが主催のモデリングフォーラム2025が11月26日にあるのに先駆けて、増田亨さんが講師のワークショップが開催されたので参加してきました。
26日の本開催の方もなかなか面白そうな発表が多いので参加する予定です。
司会はビープラウドの佐藤治夫さん。「日程調整(調整さんクローン)」をテーマに体験学習するもので、全体の流れは以下のようにざっくりしたもの。
- 適当にチーム分け
- テーマに沿って実装(3時間)
- 増田さんの講評
資料はこちら https://speakerdeck.com/haru860/lets-try-model-driven-design
増田さんは、「完成しなくてもいいけど、コードに意図をどう載せるかをみたい」ということを繰り返し言っていた気がする。
参加者全体の印象は年齢層高め、スーツ多め。モデリングフォーラムだからエンタープライズ系の参加者が多いんだろうというのはなんとなく想像しやすい。ゆるふわベンチャーはモデリングに興味ないというか、やってたとしてもフォーラムで共有する動機は少ないのかもしれない。
とはいえ、kichijojipmでよく発表しているウルフチーフの川島さんもモデリングフォーラムで発表したりするので、そんな区分けはないのかもしれないけど。
「現場で役立つシステム設計の原則」本にサインもらえた。
テーマに沿って実装
同じチームになったのは、フィンテック系で働いているフリーランスの人、大手メーカーの内製ITツール部門の若い人の3人。
調整さんみたいな日程調整するコードを3時間で作るというもの。
I/Oはなくていいと説明していた通り、アプリケーション層とドメイン層だけに集中するのがポイントだった。自分は簡単なテストランナーを作成して、それを繰り返し実行することで動作確認を続けた。
1時間半くらいで最適日を出力するところまでようやく完成。最小のモデルから作成していくのがいいというのはわかるけど、実装していかないとモデルの抽出もうまくできないので、理想の通りにはなかなかいかなかった。
最低限が完成したら、あとは流れに乗って進行して、「回答結果に△を追加」、「閾値以下は再調整」などの追加要件を実装。構造がシンプルなのでコーディングエージェントが素直に実装してくれた。
同じチームの人の日本語でメソッド名、変数名を定義というのをやっていて面白かった。けど、今考えると、日本語で定義するということと、モデル群がどう責務が分散しているかの類推を促すというのは別物なのだと気づいた。日本語で情報量を多く詰め込んでも、それ自体がモデル駆動を推進することとは違うのだなと。あらためて考えるとそれはそう。日本語でも責務が偏ったコードはいくらでも書けるし。
増田さんの講評
講評の前に実装言語のアンケートをしていて、Javaが最多、あとはC#, Python, Ruby, Goなどが少数派だった。
最初の発表の人は、astahでクラス図書いてJavaのファイルやAPIを生成、コーディングエージェントで中身を実装という流れっぽい。けど、クラス図の説明の比重が多くて、実装の説明があんまりなくて、ちょっと想像と違っていた。増田さんの講評でも、コードにもっとフォーカスして欲しいというニュアンスがあったように思う。
あと、1ファイルのコード量が多くてあんまり責務を分散できていないように見えたのと、エージェントが生成されたコードを完全に理解しないで使っているような印象を受けた。他の人のコードでも思ったけど、みんなあんまりコーディングエージェントがうまく使えていないような印象を受けた。責務が分散できていないために、詳細な指示を出さないといけないとかそういう状態になっているんじゃないかなーとか考えた。
2番目の人の講評はトイレを我慢できなくてあんまり聞けず。実装中はずっと集中してたので実装フェーズが終わってからの尿意がすごかった。streamを使っていたのでJavaだったはず。やはりJava多い。
全チームの代表を講評するのかと思ったら次で最後と言っていたので慌てて挙手して発表。増田さんに講評してもらう機会なんてそうないので発表できてよかった。
作成したコードはこのリポジトリにプッシュしてある。
https://github.com/shrkw/umtp-modeling-forum-2025-workshop
自分の感想は以下。
- 全体的に行数の短いファイル構成になっていて、責務も分散した構造にできた気がする。なかなか良いのではないか
- AvailabilityTableモデルに複雑さが集中しているのでもっと分解したほうが良さそう
- DateSlotモデルを作ったけど上手く使えなかった
- イミュータブルにしたかったけどできなかった
増田さんからのコメントはざっくり2点。
- AvailabilityStatusクラスがSCORE_MAPを持っているけど、Statusシンボル自体に意図を持たせるのがいいんじゃないか
- 独立させると、シンボル追加の際にマップ更新が不要にもなる
- テストランナーは出欠テーブルの列の単位で追加しているけど、最初は行の単位で受け付けるので良かったんじゃないか
- そのほうが最小単位の実装がしやすい
- 入力と内部構造の不一致はどうしてもあるので、それは後から考慮するのでもいいはず
前者については特によく咀嚼したくなったので、帰りの電車で考えてみて以下のようになった。
前者は元々はこういうコード。
class AvailabilityStatus AVAILABLE = '⚪︎' UNAVAILABLE = 'x' SCORE_MAP = { AVAILABLE => 1, UNAVAILABLE => 0 } def initialize(value) @value = value end def score SCORE_MAP[@value] || 0 end end
指摘の内容は「現場で役立つシステム設計の原則」本にも書いてあったけど、おそらくJavaのenum相当を想定していたのかなと想像。enum自体がシンボルになればそれが意味を持てるし、スコアを取得するためにSCORE_MAPというマッピングテーブルを使っているけど、Javaのenumなら以下のように取得できてマッピングが不要になる。
FeeType feeType = FeeType.valueOf(feeTypeName);
ということを踏まえて、次の差分を追加してみた。
このコミットだとenum相当のところがまだマッピングになってしまっていたけど、それは後で直した。
コードを抜粋すると以下。
diff --git a/availability_status.rb b/availability_status.rb index ff297d0..1815c93 100644 --- a/availability_status.rb +++ b/availability_status.rb @@ -1,23 +1,17 @@ -class AvailabilityStatus - AVAILABLE = '⚪︎' - UNAVAILABLE = 'x' - HALF_AVAILABLE = '△' +# frozen_string_literal: true - SCORE_MAP = { - AVAILABLE => 1, - UNAVAILABLE => 0, - HALF_AVAILABLE => 0.5 - } +require_relative 'availability_status_symbols' - def initialize(value) - @value = value +class AvailabilityStatus + def initialize(status_symbol) + @status_symbol = AvailabilityStatusSymbols.value_of(status_symbol) end def score - SCORE_MAP[@value] || 0 + @status_symbol.score end def to_s - @value + @status_symbol.to_s end end diff --git a/availability_status_symbols/available.rb b/availability_status_symbols/available.rb new file mode 100644 index 0000000..4847a45 --- /dev/null +++ b/availability_status_symbols/available.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +require_relative 'base' + +module AvailabilityStatusSymbols + class Available < Base + def value + '⚪︎' + end + + def score + 1 + end + end +end
module AvailabilityStatusSymbols SYMBOLS = [ AvailabilityStatusSymbols::Available.new, AvailabilityStatusSymbols::HalfAvailable.new, AvailabilityStatusSymbols::Unavailable.new ].freeze def self.value_of(symbol) SYMBOLS.find { |enum| enum.value == symbol } end end
各シンボルを区分オブジェクトとして定義してスコアもその中で持たせる。Rubyにenum機能があればenumの定義はAvailabilityStatusモデルに持たせていたけど、Rubyにはないので、AvailabilityStatusSymbolsモジュールを定義してそこに value_of の機能を持たせることにした。
こうするとたしかに、シンボル自体が意図を伝えてくるし、スコアの点数や文字表現が変更されてもコードの変更がそのシンボルモデル内に閉じられて、開放閉鎖の原則を保つことができる。
また、さらなるシンボルの追加があったとしても最小限で明示的なものになる。
例えば、マイナスのスコアを入れるとした場合もこういう差分になってこのコード自体が意図を放つ。
まとめ
ファウラーの「リファクタリング」(初版だけど)やエヴァンスの「ドメイン駆動設計」、増田さんの「現場で役立つシステム設計の原則」も読んでいたので目指すものはわかっていたつもりではあるけど、やはり実際に手を動かすとだいぶ学びが多くて楽しいワークショップでした。 限られた時間で作りきるエクササイズにもなったし、他の参加者がどういうことを考えてプログラミングをしているのかが垣間見えてとても多くの学びを得ることができました。 モデリングやモデル駆動は、これからも日々のプログラミングで実践しつつ、研鑽を高めていきたいと思います。