20151110 ドメイン駆動設計によるサービス開発

  • View
    6.719

  • Download
    0

  • Category

    Software

Preview:

Citation preview

ドメイン駆動設計によるサービス開発DDD ALLIANCE! ドメイン駆動設計をやってみた 6つの現場からの報告

自己紹介名前:大西 真央(@mmmmao0530)

組織としての役割:DDD布教活動

チームとしての役割:リードエンジニア

(技術リーダー)

最近の関心ごと:効果的なドメインモデルの作成

         チームビルディング

概要ビッグローブでDDDを導入して早2年。この2年間、 ISP事業における主要なサービスをDDDで開発してきて、試行錯誤の連続でした。

今回は、試行錯誤の過程を経て生まれた、実際に実践している

 ・設計・実装の考え方(ドメインモデルやコード例やDB設計など)

 ・チーム環境の考え方(開発プロセスやチームビルディングなど)

の2つを軸に現場でのリアルな体験を紹介します。

また、最後に、試行錯誤における失敗談も紹介します。

アジェンダ

1. 導入• Biglobe × DDD

2. チーム環境の考え方• スクラム開発• チームビルディング

3. 設計・実装の考え方• アプリケーションコード• DB設計

4. 失敗談5. まとめ

導入  Biglobe × DDD

DDDの歴史と組織

DDD 適用サービスの歴史

2013年 2014年 2015年

Wi-Fiスポット

LTE/3G音声

Biglobe電話

Biglobe光

MVNO支援サービス

リニューアル ドコモ光

LTE/3G全部

新規サービス

LTE/3G帯域制御

NINJA SIM

※初回 S-inのみ記載。各サービスは現在も開発中。

5チーム /30人規模でDDD実践中!全開発者の30%

100名規模でDDDを実践できる組織にしたい!

組織として大事にしていること全開発者でDDDの価値を共有。

現状の課題分析と課題解決に向けての行動。

人材育成計画。

新たな技術にチャレンジ。

開発環境基盤の高度化もちゃんとやる。

DDDを採用する価値

開発現場の取り巻く環境

サービス

関心事が何か?明確化

誰の関心事なのか?価値の最大化

開発現場の取り巻く環境

サービス

関心事が何か?明確化

サービスを継続することが前提

誰の関心事なのか?価値の最大化

開発現場の取り巻く環境

サービス

関心事が何か?明確化

サービスを継続することが前提

誰の関心事なのか?

使われ続ける

変更し続ける

成長し続ける

価値の最大化

開発現場の取り巻く環境

サービス

関心事が何か?明確化

サービスを継続することが前提

誰の関心事なのか?

使われ続ける

変更し続ける

成長し続ける

関わる人も変わっていく

価値が変わる可能性大

価値の最大化

開発現場の取り巻く環境

サービス

関心事が何か?明確化

サービスを継続することが前提

誰の関心事なのか?

使われ続ける

変更し続ける

成長し続ける

関わる人も変わっていく

価値が変わる可能性大

価値の最大化

課題

独自言語だとつぎはぎで機能強化していくのでサービスの維持で

精一杯

機能強化に莫大な費用がかかる

サービス終了までの

トータルコストの削減が大事

DDDを採用する価値

トータルコスト削減

DDDを採用する価値

トータルコスト削減

変更コストを下げる

属人性を下げる

DDDを採用する価値

トータルコスト削減

変更コストを下げる

属人性を下げる

業務をコードで表現

チーム環境の考え方 スクラム開発

スクラムの概要

DDD目線でのスクラム

立ち上げ

初期ドメインモデルな

スプリント ドメインモデル

1W~3W

1W

24 H

コード

リリース

2M~3M

プロダクトバックログ

スプリントバックログ

プロダクト

立ち上げ

初期ドメインモデル

業務フロー図

ユースケースフロー図

スケルトンコード

DB設計

状態遷移図

コンテキストマップ

④ ③

初期ドメインモデル

業務フロー図

ユースケースフロー図

スケルトンコード

DB設計

状態遷移図

コンテキストマップ

④ ③

ドメインモデルを成長させる

例:ユースケースフロー図

アクター

処理の流れ

ユースケース単位にどのエンティティが

何の業務をするのか設計。

主にアプリケーション層の設計。

立ち上げ時の心構え

開発初期はわからない事だらけ

開発初期はわからない事だらけ方向付けレベルの設計に留める

コアドメインを議論することはサービスを理解する第一歩

サービスの複雑性と向き合う

スプリント

ドメインモデル

ユースケースフロー図

コード

スプリントバックログ

各メンバーがスプリントバックログに着手。

ドメインモデルとコードを結びつける ↓

モデルの持つ意図をコードに引き継ぎ コードを通してモデルにフィードバック

設計者と実装者を分離しない

スプリント時の心構え

コアドメインの設計・実装は

重点的に

プルリクエストのWIPを活用して効率的にレビューを!

WIP:Work In Progress(作業中)の略。

スプリントごとにドメインモデルから重要な概念を追加不要な概念を削除

普段の会話の

ぎこちなさに敏感になる

書き言葉と話し言葉が

ズレたら警告サイン

初期のドメインモデルや実装では重要と思わなかった問題が見つかる可能性がある

見つけた問題を放置するといつか困ったことになる

ドメインモデルと実装を

改善するプロセスが大事

スクラム開発 まとめ

ドメインモデルは時間と共に進化。

立ち上げ時は方向付けレベルの設計。

スプリント内の設計活動が重要。

チーム環境の考え方 チームビルディング

SW開発におけるチームビルディングの

必要性

SW開発はチームスポーツであり技術的要因と同じだけ人的要因が

パフォーマンスに影響

チームメンバーが相乗効果を発揮

知識(要求)が絶えず変化するので知識の共有が大事

DDDにおけるチームビルディングの

必要性

本の紹介

チーミング素直に意見を言う 試みる 協働する 省察する

学習するための組織づくり境界を越えて通じ合う 失敗から学ぶ 心理的安全を生み出す 学習するための骨組みを作る

学習しながら実行する診断する デザインする 行動する 省察する 

学習しながら実行するための土台

チーミング

素直に意見を言う 試みる 協働する 省察する

学習するための組織づくり境界を越えて通じ合う 失敗から学ぶ 心理的安全を生み出す 学習するための骨組みを作る

学習しながら実行する診断する デザインする 行動する 省察する 

学習しながら実行するための土台

DDDで必須の考え方

効率を追求しながら実行する 学習しながら実行する

リーダーは答えを持っている リーダーは方向性を定める

決まったプロセスが導入される 出発点として意図的に仮の作業プロセスが設けられる

変わることは大変な労力を伴う仕事だと考えられる

絶えず少しずつ変化することが日常的になる

一方通行のフィードバックがなされる

双方向のフィードバックがなされる

社員の判断は阻止される 社員の判断は不可欠である

上司を恐れるのは普通のことである 不安があると試みや分析や問題解決が妨げられる

目標:今日にも利益を勝ち取ること 目標:長期的な価値を生み出すこと

効率を追求しながら実行する 学習しながら実行する

リーダーは答えを持っている リーダーは方向性を定める

決まったプロセスが導入される 出発点として意図的に仮の作業プロセスが設けられる

変わることは大変な労力を伴う仕事だと考えられる

絶えず少しずつ変化することが日常的になる

一方通行のフィードバックがなされる

双方向のフィードバックがなされる

社員の判断は阻止される 社員の判断は不可欠である

上司を恐れるのは普通のことである 不安があると試みや分析や問題解決が妨げられる

目標:今日にも利益を勝ち取ること 目標:長期的な価値を生み出すこと

当てはまる箇

所が多い

DDDに関連するチームビルディングの

内容

1つ目

全員で設計

メンバー全員が考える場を作る。メンバーの意見を引き出す。

メンバー間のギャップをなくす。

ファシリテーション重要

メンバー全員が

同じ地図(ドメインモデル)を理解するため

全員で設計する目的

設計の目的と効果をメンバー全員が理解して実装するため

全員で設計する目的

熟練者のノウハウを

経験が浅いメンバーに

伝授するため

全員で設計する目的

2つ目

モデリング済みの部分も積極的にモデリング

実施タイミング

 大規模な機能強化時 プランニング時 バックログの着手時 コードレビュー時 朝会後に 行き詰った時

ホワイトボードがあれば

いつでもモデリングできる

経験が浅いメンバーはモデリングする機会を増やすこ

とが成長につながる

既存部分をモデリングする目的

新規加入メンバーはドメインを理解する

チャンス

既存部分をモデリングする目的

モデリングは

メンバー自身に実施させる

大事なこと

チームビルディングの結果

チームの設計力が強化

変更コスト /属人性が下がる

設計力が強化すると

DDDを採用する価値

トータルコスト削減

変更コストを下げる

属人性を下げる

業務をコードで表現

DDDを採用する価値につながる。

チームビルディング まとめ

DDDはチームビルディングが必須。

全員で設計することが大事。

既存部分も積極的にモデリングすることが大事。

設計・実装の考え方 アプリケーションコード

組織で行ったDDD勉強会のコードを紹介

動作環境言語 :  Java8

フレームワーク :  SpringFramework

O/Rマッピング : MyBatis

DB : Oracle(ローカルはH2)

ビルドツール : Gradle

ユースケース

コンテキストマップ

ドメインモデル

入会仕様入会可能条件20歳以上である。利用可能なクレジットカードを保持している。

接続コースは「ベーシック」または「ニコニコ」で申し込む。

入会の際、会員登録情報として申込者の個人情報を必要とする。

レイヤー構造

ドメイン層

API層 アプリケーション層

インフラストラクチャ層

ドメイン層を独立させる

ドメイン層に依存させる

ここからコードで

アプリケーション層入会サービス

アプリケーション層入会審査サービス

ドメイン層会員エンティティ

ドメイン層リアルタイム審査

データソース層会員リポジトリ

データアクセス層会員リポジトリのMapper

設計・実装の考え方  DB設計

イミュータブルデータモデル

業務が発生すると、業務履歴をひたすら記録

( insert)

これだけ

とある業務に対する変更業務も

当たり前のように記録( insert)

変更業務でUpdateすると記録の改ざん

これは、極論です(笑)

業務を中心に捉えると業務ごとにデータが残る方が自然

現実はInsertオンリーなDB設計は難しい

テーブルの種類

Eventテーブル業務が発生するとひたすら insertしていくテーブル。

Stateテーブル業務が発生すると必要に応じて updateしていくテーブル。ステータスなど最新の情報を管理。

Eventテーブルが中心。

Stateテーブルは補助。

Stateテーブルの使いどころパフォーマンス改善が図れる場合。

DB側で業務イベントの発生ルールを完璧にコントロールしたい場合。(詳細は後程。)

Stateテーブルはデータアクセス層

の関心ごとで利用

イミュータブルデータモデルを徹底すると

副次効果としてNULL項目が減る!

具体的な話

DDD勉強会のDB設計

biglobeID入会日

biglobeID(FK)退会日

入会 退会

会員に関連するテーブル

abc123452015/10/10

abc123452016/10/10

退会イベント入会イベント

入会 退会

業務が発生すると、関連するテーブルに業務履歴を記録( insert)

biglobeID入会日退会日

会員

ミュータブルデータモデル

だけ

abc123452015/10/10null

abc123452015/10/102016/10/10

会員 会員

update

退会イベント入会イベント

もう少し難しく

契約エンティティに対するDB設計の場合

申込中

契約中

解約予約中

解約済

申込キャンセル

申込 ID申込 ID(FK)

申込契約

申込 ID(FK)

キャンセル

解約 ID申込 ID(FK)解約日

解約

解約 ID(FK)解約キャンセル

申込 ID申込 ID(FK)

申込契約

申込 ID(FK)

キャンセル

解約 ID申込 ID(FK)解約日

解約

解約 ID(FK)解約キャンセル各テーブルの外部キーにより

業務イベントの流れをコントロール

これにStateテーブルを足すなら

申込 ID申込 ID(FK)

申込契約

申込 ID(FK)

キャンセル解約 ID申込 ID(FK)

解約

解約 ID(FK)解約キャンセル

申込 ID(FK)

ステータス

申込 ID申込 ID(FK)

申込契約

申込 ID(FK)

キャンセル解約 ID申込 ID(FK)

解約

解約 ID(FK)解約キャンセル

申込 ID(FK)

ステータス

契約とキャンセルは排他な関係。

契約とキャンセルが同時に発生した場合、アプリケーションコードと Eventテーブルだけで、この制約を実現できない。

Stateテーブル更新時のWhere句でステータスも条件(ステータス=申込中)に入れることにより、この制約を維持できる。

申込 ID申込 ID(FK)

申込契約

申込 ID(FK)

キャンセル解約 ID申込 ID(FK)

解約

解約 ID(FK)解約キャンセル

申込 ID(FK)

ステータス

解約キャンセルするユーザが少ないと判断して、 Stateテーブルは導入しない。最新の解約レコードから判断。

失敗談

ドメインモデル関連ドメインモデルが古い

ドメイン層関連Policyクラスの乱立

データアクセス層関連Repositoryの saveメソッド

他集約の Repositoryを操作

SQLに業務ロジックが混在

失敗談 ドメインモデルが古い

ドメインモデルが古い内容日々変化するドメインモデルが最新化されない。

問題点値オブジェクトを全部管理していたので、更新が追い付かない。最新のドメインモデルが把握できない。

対策最初に作成するドメインモデルと Keepするドメインモデルを分離。Keepのドメインモデルは重要な概念や本質のみ。

ドメインモデルが古い

最初に作成するドメインモデル

ドメインモデルが古い

Keepするドメインモデル

失敗談  Policyクラスの乱立

Policyクラスの乱立内容業務チェックのために、ドメイン層に Policyクラスを用意。

問題点エンティティから業務ロジックが抜けていく(エンティティ貧血症)。

対策基本的には、エンティティに業務チェックを記述。明示的にクラス化したい業務チェックのみ、専用のクラスを用意。

Policyクラスの乱立

Policyパッケージを作った時点で負け

失敗談  Repositoryの saveメソッド

Repositoryの saveメソッド内容永続化のメソッドを saveメソッドのみで対応。

問題点テーブル構成がドメイン層に浸食。saveメソッドでどのテーブルが登録・更新されるか不明。saveメソッドをメンテナンスできるメンバーが限定。

対策業務イベントごとに専用のメソッドを用意。

失敗談 他集約の Repositoryを操作

他集約の Repositoryを操作内容他集約の Repositoryをデータアクセス層で操作。

問題点アプリケーション層のコードだけでは関連する集約が把握できず、全てのコードを読む必要がある。

影響範囲の特定に時間がかかる。ミスする。

対策他集約の Repositoryは操作しない。

失敗談  SQLに業務ロジックが混在

SQLに業務ロジックが混在内容SelectのWhere句に業務ロジックが記述。

問題点業務ロジックがドメイン層から流出。いつかの仕様変更でバグの温床に。

対策可能な限り業務ロジックはドメイン層に集約。

検索条件は主キーのみにしてステータスの絞り込み条件はド

メイン層で記述

まとめ

DDD本と現場での実践を行ったり来たりして

理解を深めていくことが重要

サービスが存在する限り

変化し続け成長し続け

価値を届け続けるソフトウェアを作ることが重要

ご静聴ありがとうございました。