Slide 1

Slide 1 text

© DMM © DMM 破壊せよ! データ破壊駆動で考える ドメインモデリング 2024/10/25(金) 合同会社DMM.com プラットフォーム開発本部 ミノ駆動

Slide 2

Slide 2 text

© DMM 自己紹介 ミノ駆動 ( @MinoDriven ) 合同会社DMM.com プラットフォーム開発本部 第3開発部 DeveloperProductivityGroup DMMプラットフォームの設計を改善し開 発生産性向上を図るのがミッション 2

Slide 3

Slide 3 text

© DMM もうちょっと具体的な仕事の内訳 サービス全体で共通利用されるDMMプラットフォーム(アカウント管理、不 正対策、決済、DMMポイント etc..)の設計品質改善 ● 設計ガイドライン策定 ● チームへの設計指導 ● プラットフォーム所属のエンジニア(約120人!)に対する設計勉強会 ミノ駆動本を使ったワークショップ形式の実践講習 ● リファクタリング体制整備 3

Slide 4

Slide 4 text

© DMM 著書紹介 『良いコード/悪いコードで学ぶ設計入門』 変更容易性の高い設計を学ぶ、 初級〜中級向け入門書 ITエンジニア本大賞2023技術書部門大賞受賞 12刷重版 4

Slide 5

Slide 5 text

© DMM 皆さんバグは好きですか?

Slide 6

Slide 6 text

© DMM 嫌いですよね

Slide 7

Slide 7 text

© DMM でも バグを発生させない設計 ちゃんとやってます? 本日はそういうお話です

Slide 8

Slide 8 text

© DMM 恐怖 注文数の事例(実話)

Slide 9

Slide 9 text

© DMM とあるショッピングサイトにて 本番環境に対してセキュリティ的に問題 がないか検証することになった。 インフラ面やログイン認証などいろいろ 確かめてみたが、特に問題は見当たらな かった。 ところが…… 9

Slide 10

Slide 10 text

© DMM マイナス注文数と返金 注文画面で注文数に「-1」を入力し注文実行し たところ、異常を知らせるような表示もなく、 すんなり注文できてしまった。 後日経理の人がすっ飛んで来て、「注文した人 に返金するよう返金指示書が発行された。そ の人は今いるか」といった奇妙なことが発生。 注文した人に返金!? もうヤバいですねこれ。 10 総額: $-39 注文数: -1 カートに入れる 今すぐ購入

Slide 11

Slide 11 text

© DMM 注文総額を好きなように減額する裏技 このマイナス注文数による返金があったか 調査したが見当たらなかった。 しかし別の不正が大量にあったことが発覚。 注文の際、買い物カゴの中に欲しい商品と 一緒に、マイナス注文数の商品を入れること で注文総額を減額する、という裏技だった。 この手口は噂として広まっていて、かなりの 不正が発生していた…… 11 総額:$207 価格:$246 注文数:1 価格:$39 注文数:-1

Slide 12

Slide 12 text

© DMM 詳しくは書籍『セキュア・バイ・デザイン』を読もう 12 マイナス注文数に関する詳細が解説されています

Slide 13

Slide 13 text

© DMM 皆さんが開発しているサービスは大丈夫ですか? 不正な値から守るロジックになっていますか? 実はヌケモレがあったりしませんか?

Slide 14

Slide 14 text

© DMM 壊れたデータによるバグ バグの原因はさまざま。 その中でも、不正なデータ、壊れたデータによるバグが多くを占めます。 ● 外部から不正な値を入力される ● ロジックが正しくないために、壊れたデータが内部的に作られてしまう だから本日はセキュリティの話ではありません。 データ全般の異常系に関する設計の話なのです。 また、壊れたデータがDBに保存されてしまうことがマズいので、POSTや PATCHといった更新系リクエストを対象とします。参照系は対象外です。 14

Slide 15

Slide 15 text

© DMM 異常系は後手に回りがち こういうの多くないですか?身に覚えはありませんか? ● とにかく動くものを早く作る。 ● データが壊れるようなバグが出たら付け焼き刃的にバリデーションを 実装。 ● そのバリデーションもUtilなどいい加減な箇所に五月雨に実装。 ● バリデーションの呼び忘れで同じバグが発生することも。 機能開発時、正常系のことばかり考えてしまい、異常系がおざなりになっ たり、後手に回ることが多くはありませんか? 15

Slide 16

Slide 16 text

© DMM データ異常系の設計を ちゃんとやりましょうね、 という話です

Slide 17

Slide 17 text

© DMM 解決編

Slide 18

Slide 18 text

© DMM 解決したい問題を整理 以下2点の問題を解決する必要があります。 1. 異常系のヌケモレ 2. バリデーションなどの異常系対策ロジックが バラバラに実装される問題 18

Slide 19

Slide 19 text

© DMM データ完全性

Slide 20

Slide 20 text

© DMM データ完全性 データに欠損や不整合がなく、正常な状態を維持すること。 つまりデータ異常の対策には、 データ完全性を保証する設計が必要なんですね。 20

Slide 21

Slide 21 text

© DMM 問題解決に必要な設計 1. データ完全性を保証する設計 2. (完全性保証用の)バリデーションが バラバラに実装されるのを防ぐ設計 この2つを同時に設計できるとても良い設計があります。 それが…… 21

Slide 22

Slide 22 text

© DMM DDD

Slide 23

Slide 23 text

© DMM ドメイン駆動設計だと思った?

Slide 24

Slide 24 text

© DMM データ破壊駆動 (Data Destroy Driven)

Slide 25

Slide 25 text

© DMM データ破壊駆動 クラッカーになったつもりで以下を検討します。 ● どうやったらクラス内のデータ(インスタンス変 数)を破壊できるか ● どうやったら矛盾したデータを作れるか ● どうやったらユーザーが困る状況に陥るか この検討をすることで、正常な値を保証する、即ち 完全性を保証する制約が洗い出されます。 25 Let’s 破壊駆動!!

Slide 26

Slide 26 text

© DMM 26 // 金額クラス public class Money { public int amount; public Currency currency; } Moneyクラスのインスタンス変数を破壊してみよう

Slide 27

Slide 27 text

© DMM 27 // 金額クラス public class Money { public int amount; public Currency currency; } ● amount(金額値)をマイナスにできてしまう ● 別の金額にすり替えることができてしまう ● currency(通貨単位)がnullはありえない ● 途中で別の通貨単位に変更されるのは問題

Slide 28

Slide 28 text

© DMM 28 // 金額クラス public class Money { public int amount; public Currency currency; } 【制約】 ● amountは0以上 ● amountは不変 【制約】 ● currencyは非null ● currencyは不変

Slide 29

Slide 29 text

© DMM 29 class Money { int amount; Currency currency; } class Validator { static boolean validateMoneyAmount(Money money) { return 0 <= money.amount; } } ダメなのはこういう構造。 せっかく制約を洗い出せても、これではMoneyクラ ス単体で完全性を保証できない!壊れたデータが入 り込む余地がある! 大した根拠もなく「バリデーションは別に実装するも のだ」と固く信じている人が一部にいるので注意。 ダメ絶対

Slide 30

Slide 30 text

© DMM 30 カプセル化 カプセル化とは、データとそのデータを操作するロジックをひとつにまとめ ること。 クラスはカプセル化の手段のひとつ。 (golangでは構造体、Reactではコンポーネント) 「クラスって何のためにあるの?」と聞かれたら「カプセル化のためで す!!」と即答できるようになりましょう。 クラスでのカプセル化とは、インスタンス変数とそのインスタンス変数を操 作するロジックをひとまとめにすること。 クラス自身で(←ココ大事)完全性を保証できるよう、 そのクラスに関連ロジックをカプセル化しよう。

Slide 31

Slide 31 text

© DMM 31 // 金額クラス class Money { final int amount; final Currency currency; Money(final int amount, final Currency currency) { if (amount < 0) throw new IllegalArgumentException(); if (currency == null) throw new IllegalArgumentException(); this.amount = amount; this.currency = currency; } } finalを付与し不変に 完全コンストラクタ。 インスタンス生成時に値チェックし、 初期化。 Moneyクラス単体でデータ完全性を保証できるようにする

Slide 32

Slide 32 text

© DMM テストコードもデータ破壊駆動で! 32 @Test void testNegativeAmountThrowsException() { assertThrows(IllegalArgumentException.class, () -> { new Money(-1, Currency.getInstance(Locale.JAPAN)); }); } 境界値テスト。 異常値を入れて例外がスローされる ことを検証する。

Slide 33

Slide 33 text

© DMM ここまではバグを発生させないための 異常系対策設計の話です

Slide 34

Slide 34 text

© DMM もうひと手間加えて 変更容易性が向上するようにします

Slide 35

Slide 35 text

© DMM 35 class CommonManager { int addMoneyAmount(Money money1, Money money2) { return money1.amount + money2.amount; } } 金額加算ロジックがMoneyとは全然関係ない箇所に実 装されている。金額関係のロジックがどこに実装されて いるのか把握が難しくなり、変更容易性が低下する。

Slide 36

Slide 36 text

© DMM ドメインモデルの完全性

Slide 37

Slide 37 text

© DMM ドメインモデルの完全性とは ドメインに関するデータやロジックに欠損や不整合がなく、正常な状態を維 持していること。 データ完全性を踏まえた設計は、データが壊れないように保証することは できる。しかし機能性までは保証できない。 ある概念に強く関係する機能が欠損したり、どこか別の箇所に実装されて しまう懸念がある。 37

Slide 38

Slide 38 text

© DMM // 金額クラス class Money { (中略) Money add(final Money other) { if (!currency.equals(other.currency)) { throw new IllegalArgumentException(); } final int added = amount + other.amount; return new Money(added, currency); } } 38 金額加算メソッドもMoneyク ラスにカプセル化する。 これでドメインモデルの完全性 が満たされる。 ドメインモデルの完全性を満たすことで変更容易性が向上する

Slide 39

Slide 39 text

© DMM 39 ドメインモデルの完全性 データ完全性 完全性はこのような関係にあります。 ドメインモデルの完全性だけで説明がつきますが、データ異常によるバ グ発生に対処するため、あえてデータ完全性を話題に出しました。

Slide 40

Slide 40 text

© DMM 40 メール設定 メール設定ID ユーザーアカウントID プライマリメールアドレスID メールアドレス メールアドレスID アドレス文字列 * GitHubっぽいメール設定のモデル。 複数メールアドレスを追加できる。 どれか1つをプライマリに設定できる。 この手のドメインモデリングではデータ だけが洗い出されがち。もっと大事な のは制約!! データ破壊駆動で制約を洗い出してみ よう。 ドメインモデルの完全性の観点でモデ ルに必要な機能を洗い出してみよう。

Slide 41

Slide 41 text

© DMM 41 メール設定 メール設定ID ユーザーアカウントID プライマリメールアドレスID メールアドレス メールアドレスID アドレス文字列 1..* (null)も含め不正なIDだと困る フォーマットに従っていないアドレスだと困る IDやアドレスを変更されると困る (nullも含め)不正なIDだと困る メール設定IDやユーザーアカウン トIDを変更されると困る プライマリメールアドレスに他人のア ドレスIDを設定されると困る メールアドレスの追 加、削除ができる必 要がある メールアドレスを全部削除されると困る プライマリメールアドレスを削除されると困る 同じメールアドレスを追加されると困る データ破壊駆動で考えるだけでこれだけの制約が洗い出される

Slide 42

Slide 42 text

© DMM 42 弊社DMMでデータ破壊駆動をやってみた とあるチームの新規機能開発で、ミノ駆動が設計支援に入りました。 設計フェーズでドメインモデリングを実施し、そこでデータ破壊駆動をやっ てみました。本スライドにあるように「クラッカーになったつもりで、どう やったらモデルのデータを壊せるか、矛盾したデータにできるかを考えて みましょう」と指示。 すると、以降ミノ駆動があれこれ何も言わなくてもチームメンバー同士が 自律的にどうやったら壊せるかを議論し始め、あっという間に制約を洗い 出すことができました。そして頑強なコードを実装できました。 効果てきめんです!!

Slide 43

Slide 43 text

© DMM まとめ ● バグは、データ異常が原因のものが多くを占める。 しかし異常系の検討は後手に回りがち。 ● データ破壊駆動で「どうやったらデータ(インスタンス変数)を破壊でき るか」を考えてみよう。破壊されないための制約を洗い出そう。 ● クラス自身で完全性を保証できるように制約をカプセル化しよう。 ● ドメインモデル完全性の観点から、必要なドメインロジックをカプセル 化し、変更容易性を高めよう。 ● ドメインモデリングの段階からデータ破壊駆動で設計しよう。 43

Slide 44

Slide 44 text

© DMM 皆さんにあらためて問います

Slide 45

Slide 45 text

© DMM バグは好きですか?

Slide 46

Slide 46 text

© DMM 深夜緊急対応は好きですか?

Slide 47

Slide 47 text

© DMM 致命的障害が発生すると サービスや会社の信頼を著しく損ねます そしてサービスが使われなくなります

Slide 48

Slide 48 text

© DMM サービスが使われないと お金が入って来なくなります

Slide 49

Slide 49 text

© DMM そしてあなたの給料が上がらなくなります

Slide 50

Slide 50 text

© DMM そうした 恐ろしい事態を起こさないために

Slide 51

Slide 51 text

© DMM 今すぐデータ破壊駆動で モデルを点検しましょう 正しいデータを維持できるよう バリデーションなどをモデルにカプセル化しましょう

Slide 52

Slide 52 text

© DMM ご清聴ありがとうございました