読者です 読者をやめる 読者になる 読者になる

yoskhdia’s diary

DDDとかプログラミングとかアーキテクチャとか雑多に

DDD座談会に際して素振りをしていた

ddd-zk.connpass.com

DDD座談会のパネラーとして登壇させていただきました。

素振り

予め参加者の方々からいただいた質問(テーマ)に対して、予行練習というか、ちょっと自分の考えを先にまとめていたのですが、予想通りテーマひとつひとつが盛り上がったので全部を話しきることができませんでした。 折角なのでここに公開しておきます。 ところどころ、座談会終わっての後書きも追記しています。

なお、あくまで私見ということでよろしくお願いします。座談会のまとめ記事というわけではありませんので、悪しからず。
各人のコンテキストで、こんな解釈あるよ等あると思いますので、Twitterでもコメントでもフィードバックいただければ幸いです。


DDD全般について

DDDに向いている要件とは?費用対効果ってどうなの?

継続的に投資したいプロダクトであることが大事だと思っています。

システムは一度作れば費用はかかり続けます。 その費用を成長のための投資と捉えられるかどうか、は重要だと考えます。 また、初期投資という面では、その長いシステムのライフサイクル全体で考える必要があるでしょう。 一般にシステムは使用期間が長くなるほど、仕様変更や機能追加によってコードの複雑度は増大していくと思います。 場合によっては、どこかを直せばどこかが壊れるようなケースもあります。(たまに聞こえてきますね。) そのリスクを予めヘッジする考えとしてDDDを採用する交渉をしてみるのも手かもしれません。

短期で不変で小規模なシステムで開発者が未熟なら過剰と思います。 また、システムが完成したらチームが解散されて、保守要員が別にアサインされるような環境は避けたいところです。 DDDを使わない開発よりは幾分マシな気もしますが、文化を継承せずに新しいチームでシステムを引き継ぐのは辛いことの方が多いのではないでしょうか。 文化は一朝一夕では継承されません。 組織としてのあり方も問われますね。

私の場合、ベンチャー企業に身をおくので、この視点で考えると、プロダクトの超初期にDDDでスタートできていれば、今のような成長の2段階目でリプレースすることももう少し容易だったのかなと思うこともあります。 たらればの話にしかなりませんが、ドメインモデルと(高負荷への対策のような)非機能要件の話は分けて考えることができるはずで、ドメインモデルは据え置き(もしくは省力)でシステムのスケーラビリティを上げることに注力できたのではないかと考えてしまうこともあります。 実際にはそんな上手く移行できないと思いますし、ベンチャーである以上は、まずプロダクトが市場に受け入れられるのか?を得ることが最優先ではあるのですけれど。

(※後書き)原田さんの仰った通り、DDD云々の前に市場に出すことが大事ですし、DDDでやれる練習をしておくことも大事ですね。自分は練習がまだまだ不足していたので、今も戦ってるまっ最中なわけで反省するところです。その意味ではDDDがもっと広がって、教養の一つみたいになれば、こういった初期からDDDというケースも増えていくのかなと思う次第です。

さらに被せれば、DDDは継続的にコードを直していくので、静的型付けチェックができる言語の方が有利に思います。 特に規模を大きくすることを考えてプロダクトをデザインしたりするので、将来のコードベース、メンバーの増加を考えても静的型付け言語を推奨したいところです。 例えば、リファクタリングIDEの力を借りてかなり省力化できますし、些細なミスもコンパイル時に防ぐことができます。 本来注力したいのは、モデルを育てることで、テストコードもシナリオを満たしているかをチェックするために書きたいと思うのです。 改修する際に、コンパイル(と型)というものがあるだけで、随分考えなければいけない・気をつけなければいけないことが減るというのが実感です。

DDDを採用して失敗したこと or 後悔したことって何?

さぁ、DDDだ!となって、あれ?どう進めれば良いんだろう?となったことです。 DDDで述べられている内容には、要件をどう整理していけば良いなどのプロセスは入っていません。 ここは別のもので補う必要があると思います。 最近読んで良かった本にあげたICONIXのような"モデルをまず捉えながらデザインしていくプロセス"を押さえておいたほうが良いです。 また、ユーザストーリーマッピングのような技法もかなり役にたつと思います。

(※後書き)原田さんは「DDD(XP)にはリズムが抜けている」と仰っていましたが、増田さんは「XPにもリズムはある、日中働いて夜はちゃんと寝ることだ」とも仰っていましたね。Scrumでなくても良いのですが、イテレーティブな取り組みができるフローはDDDを採用するなら考える必要がありますね。ICONIXもプロセスの最後に振り返りと次への反映があります。

チームメンバーが最低限持つべき知識やスキルって何?

2つあげておきます。

  • モデリングするスキル
    DDDでは開発者も主体となって分析・モデリングをしていくことが求められるので、ここは最低限必要なように思います。 もっとも最初から熟練している必要はなくて、作りながら、成長させながら勉強すれば良いので、最初のスタートがきれるくらいでOKと思います。

(※後書き)ワークショップなどで練習することは大事ですね。「シナリオをつくり、モデルをつくり、コードを書く」という3つセットでやる方が良いらしいです。2つだと正しいかを気づきにくいですが、3つだと自分でも気づける、とのこと。自分も社内で取り組んでみたいなと思いました。

  • チームに真摯でいること
    (チームは開発者だけでなく、ドメインエキスパートなども含む。) よく話し合って、よく考え、より良い未来へ向かうには、真摯でなければならないと思っています。 たまには疲れてしまうこともあるでしょうが、HRTの教訓だけは忘れてはいけないと日々思います。

HRT

http://blog.livedoor.jp/lalha/archives/50496623.html

  1. 謙虚(Humility)
  2. 尊敬(Respect)
  3. 信頼(Trust)

ドメインに対する洞察が深まった時の事例はどんなものですか?

具体的な事例は今の時点では弊社からは出せないです。。。

劇的な発見!のような体験は無いですが、あ、これそういうことだったのか!ということは度々あります。 特にドメインエキスパートが考えていることを、今求められているもの+実は将来こういうことを見据えている、という面まで得たときです。 一つ言えるのは、会話することが大事ですし、その会話をしやすいような空気であることが必要です。 雑談ばっかりじゃ仕事は進まないけれど、雑談もできない空気でも仕事は進まないと思います。


DDDと開発プロセスについて

ICONIX良いかもと思っているものの、要件定義の段階は定義されてないので、ユーザストーリーマッピングを組み合わせたいと思っています。 特にベンチャーに身をおいているので、プロダクトデザインとシステムのデザインが一致していることが競争力になると肌で感じます。 この摺り合わせにユーザストーリーマッピングに魅力を感じています。 (まだ導入できていないので、どこかで試せないかと考えているところ。)

まだ書籍「カンバン仕事術」を読めていないものの、先日の出版記念イベントで良さそうだなと思ったのでタスクボードは最近導入してみました。 これによって、今誰が何をやってるというのがわかるので、どこで停滞しているなどが分かるようになりました。 ユーザストーリーマッピングで作ったストーリーの付箋がそのまま流れてくるようなイメージでできそうと考えています。


DDDの戦略面について

ユビキタス言語が日本語で制定されている場合、コードに落とす時にどうしてるの?英訳?日本語コード?

最初から英語にできることが望ましいとは思うものの、自分も含めハードルは感じます。 特に業務用語は迷いやすい印象です。 次善策としては、極力英語の単語を使ってコミュニケーションし、日本語になっちゃうところだけ翻訳辞書を用意する辺りでしょうか。 (業界は違いますが)海外赴任経験のある人の話を聞くと、日本人チームのなかでそうしておくことで、わりとなんとかなったとのこと。

(※後書き)座談会後にふわーと聞こえてきた話としては、日本語のままコーディングするのは、最初は違和感あるけれど、伝わりやすさはかなりあるのでこれも手だよね、というものがありました。Railsでそれをやろうとすると辛そうですけれど。

ユーザやアカウントのようなどこでも利用されるドメインモデルはどのように扱っているのか?コンテキストの統合や分割の話題について

シェアードカーネルのようなライブラリ化はしない方が良いのだろうな、という印象です。 それぞれの文脈(コンテキスト)の中で意味が同じであり続けることが大事で、コード重複について考えるのは二の次です。 将来に渡って常に同期されることが予想されているのであれば、DCIのようなデータと振る舞いを分離してデータ部分は再利用できるようにすることも考えられなくはないのかなと思ったりもしますが、その辺どうなんでしょう。

例えば、マイクロサービスのような形で、シェアードカーネルをやろうとすると、あっちのデプロイに引きづられる、のような話になると思うので、コンテキストで閉じた方がスピード感は落ちない気がします。 (過去に外部システムとのデータ連携をいくつかやったことを振り返ると、)サービス間のやり取りでは同じインスタンスを使えるようにするより、腐敗防止層なりで変換をかけるほうが壊れにくくなる実感があります。 自分のところは自分の都合でできた方が良いのではないのかなぁというところです。 コミュニケーションパスが増えるほどつらくなるのでは?

(※後書き)座談会中でも盛り上がったところですが、ユーザというものを更に考える・捉え直す必要はありそうです。本来管理したい情報は何なのか?昔はログインが一番最初でしたが、現代のシステムではログイン以前にトラッキングがあるケースもあります。ユーザとは何かを今一度考えた方が良いですね。(自分も含め)


DDDの戦術面について

具体的なプロジェクトやパッケージ構成を知りたい

Clean Architectureに則っています。

https://speakerdeck.com/yoskhdia/ddd-plus-clean-architecture-plus-ucdom-fullban#38

  • domain
  • contract
  • usecase
  • adapter
  • (external)

ドメインサービスやドメインイベントをどうのようなケースで利用するのか?

ドメインサービスは、ある振る舞いをするには、複数のエンティティを使う必要があって、また、それがエンティティ自身の振る舞いにするには言葉が不自然になるとき。

ドメインイベントは、今の処理ラインとは全く別に何かをする必要があるとき。非同期であり、かつ、ゆるいつながりのものに使います。(片方が片方の処理結果には関与しない。あくまで「通知」) IDDD本では、「バックログにアイテムがコミットされたら、コミット先のスプリントやその他関係者に通知する」が例にあげられています。

RDBMSやキャッシュなどの異なるストレージをどのように扱っているのか?リポジトリの実装の話題

Repositoryは集約の単位に作るので、まずこのインタフェースを決めます。 このインタフェースには、多くは単純なCRUD操作ですが、パフォーマンスのためなど場合によっては特別なメソッドを用意することもあります。 (IDDD本でも、Repositoryに問い合わせた結果をファーストクラスコレクションに詰めたり値オブジェクトで返すことは普通でしょ?という記述はあります。) インタフェースがあれば、あとはその実装クラスをつくるだけです。 このとき、Clean Architectureなどの層の分離ができていることが重要です。

集約をただしく設計できていれば、トランザクション境界はそのメソッドで閉じることができるので、例えばRDBMS実装とキャッシュ実装があっても、トランザクション制御はRepositoryの実装クラスに閉じることができます。 補足すれば、複数の集約にまたがって整合性が必要な場合は、結果整合性を考えなければいけません。 結果整合性ではどうしてもいけない、という場合は、その集約の設計が誤っている可能性があります。 ただ、お客様の要望で、みたいな話も想像できますが、何故結果整合性でも良いのかを説明するのは開発者の責務なので、がんばりましょう。

(※後書き)個人的には原田さんの在庫管理にロックは不要、というのが眼から鱗でした。過去に関わった案件ではロックが当たり前に使われてたので、必要なものなんだろうと受け入れていましたが、今振り返るとこれは思考停止だったように思います。結果整合性でよくするには業務フローを見なおすことが必要です。ドメインにはシステムだけでなく人を織り込んでいるのかが大事だよとゴウさんも仰っていました。頭では分かったつもりでしたが、本当にはちゃんと理解できていなかったんだなと過去を振り返っています。(もっとも受託ヒエラルキーの問題もあったように思いますが...)

オブジェクトモデルとテーブルモデルのインピーダンスミスマッチをどのように解決するのか?

Repositoryなどのマッピング側でがんばりましょう。 両方で求めている方向が違うことの方が多いので、その痛みは受け入れた方が悩みは減ります。 (そして、作業が増える...) 根本解決ではなく、省力化の視点で検討してはどうでしょうか。 また、理想をいえば、DBテーブルのリファクタリングも必要なんだろうなと思います。

ドメインイベントによる結果整合性を重視する設計では、失敗の通知やUIの設計が難しくなるがどのように解決するのか?

フロントに通知されるタイミングですでに別画面に遷移してしまっている、というケースにしても、Webの特性上完全にコントロールはできないので、どうしても通知が重要なのであれば、SPAやWebSocketを検討するとかでしょうか。 これは、通知される情報の種類にもよる気がします。 失敗といっても色々な失敗があって、登録できなかったという程度なら結果が見れないだけなので、そこまで重要でないように思います。 例えば、ログインに失敗しているのに、先の管理画面に進めてしまうというようなものはダメですが、こういう場合、次の画面に進めるのはサーバサイドでリダイレクト返したり認証情報保持したりして制御しますよね。 前者の通知はUX設計にも関係しますね。

(※後書き)アトミックな操作が必要なシーンの方が実は少ないので、「don't tell, just request.」な口を考えた方が良いですね。増田さんの仰っていたように要求と実装技術のねじれは開発者が気付けるところです。ただ、それが上から降りてくるような場合には政治も関係してくるので辛さがありますね。余談ですが、SPAはク○という呟きが聞こえたような気のせいなような。