Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

DDDとクリーンアーキテクチャでサーバーアプリケーションを作っている話

10,004 views

Published on

JJUG CCC 2018 Spring の発表資料です。 #jjug #ccc_a8

Published in: Software
  • Be the first to comment

DDDとクリーンアーキテクチャでサーバーアプリケーションを作っている話

  1. 1. DDDとクリーンアーキテク チャでサーバーアプリケー ションを作っている話 #jjug_ccc #ccc_a8 JJUG CCC 2018 Spring 2018/05/26
  2. 2. 自己紹介 • 株式会社ジャストシステム 福嶋 航 • Twitter @fukushiw • Java歴約20年、JavaでWebサービス作っています • #Java100 本ノックの人 https://github.com/JustSystems/java-100practices
  3. 3. このセッションについて • 現在進行中の開発プロジェクトでの試行内容を共 有いたします • 未リリースのWebサービスのため、サービス名称や サービスの内容は明らかにしていません • DDD(ドメイン駆動設計)やクリーンアーキテクチャを 採用しているつもりですが、間違いがあるかもしれま せん • 試行錯誤過程にあるため、ぜひセッション後や懇親 会でぜひ情報交換させてください
  4. 4. セッションの流れ 1.DDDやクリーンアーキテクチャを 採用した理由 2.設計段階で行ったこと 3.実装前に準備したこと 4.実装してみてわかったこと 5.やってよかったことと変えたいこと 6.まとめ
  5. 5. セッションの流れ 1.DDDやクリーンアーキテクチャを 採用した理由 2.設計段階で行ったこと 3.実装前に準備したこと 4.実装してみてわかったこと 5.やってよかったことと変えたいこと 6.まとめ Java っぽい ところ
  6. 6. 1 DDDやクリーン アーキテクチャを 採用した理由
  7. 7. 突貫工事はうまくいかない • コンセプトは決まった • 主要な機能について、仕様概要は決まった • コア機能のいくつかはパーツが存在していたので それらを使って動くものが早く見たい • 機能仕様を作りながらプロトタイピングを進めた • いろいろ押し押しになりプロトタイプがいつの間に かプロダクションコードになりそうになった このプログラムはリリースされなかった。 いや、リリースできなかった。
  8. 8. 当時のシステム構成図(模式図) THE WEB SERVER コア機能 外部連携 THE RDB キャッシュ キャッシュキャッシュ
  9. 9. もろもろの反省と対策 過去の 失敗 対策 サーバから全データをクライア ントにダウンロードして処理 全コンポーネントが外部連携 先のデータモデルと強く結合 ビジネスロジックの主体となる コンポーネントが不在 • 異常なトラフィック量 • クライアント処理負荷増 • 外部連携先の仕様変更 の反映が全コンポーネント に影響 • 各コンポーネントはコアロ ジックに集中できず変換 処理を負担 必要なデータを必要なだけ 処理する データ統合を廃止し各コン ポーネントで独立してDBアク セスする ビジネスロジックを独立したコ ンポーネントとする ドメイン駆動設計+クリーンアーキテクチャを導入 ※社内での説明資料をほぼコピーしています
  10. 10. 2 設計段階で 行ったこと
  11. 11. 知識をつける
  12. 12. 設計の順番 ドメイン駆動設計の進め方は顧客業務(ドメイン)を分析し、それをどう解決(境界 づけられたコンテキスト)するかを決めることである。これを実践するために以下の順序 で分析・設計を進めた。 (このサービス)とは何であるか(エレベーターピッチ)を定義 機能仕様・利用シナリオを定義 ロバストネス分析 境界づけられたコンテキストの抽出 コンテキストマップの作成 非機能 要件も 定義 ※社内での説明資料をほぼコピーしています
  13. 13. 設計の順番 ドメイン駆動設計の進め方は顧客業務(ドメイン)を分析し、それをどう解決(境界 づけられたコンテキスト)するかを決めることである。これを実践するために以下の順序 で分析・設計を進めた。 (このサービス)とは何であるか(エレベーターピッチ)を定義 機能仕様・利用シナリオを定義 ロバストネス分析 境界づけられたコンテキストの抽出 コンテキストマップの作成 非機能 要件も 定義 ※社内での説明資料をほぼコピーしています 次のページから このあたりを説明
  14. 14. ロバストネス分析 • ロバストネス分析は、ユースケースのように文章 で記述された要求から分析レベルのオブジェクト を見つけ、適切な単位にまとめることができるもの です。 https://www.ogis-ri.co.jp/otc/hiroba/technical/RobustnessAnalysis/RA1/ より引用
  15. 15. ロバストネス分析をどう使ったか • あらかじめユースケースシナリオを作成しておく • シナリオを満たす機能をどう作ったらよいか考える • バウンダリ、エンティティ、コントロールでそれを表 現する バウンダリ・・・ 外部とのインタフェース。○○画面とか、cronなどを割り当てた。 エンティティ・・・ 管理するデータ。RDBなどに保存するものなどを割り当てた。 コントロール・・・ 処理。ユーザー認証とか、値の取得などを割り当てた。
  16. 16. ロバストネス分析の成果物(一部) • ノートに手書き • 書いては消し • 二人で分析し 良さそうな方を 採用
  17. 17. コンテキストマップ • ロバストネス図で出てきた機能について、似たも のを集めて「境界づけられたコンテキスト」を規定 • 書いて→直して→書いて→直して • コンテキスト間の関係について慎重に判断した • パートナーシップ • 顧客/供給者 • 順応者
  18. 18. (コア) (業務) (メイン1) (業務) (メイン2) (業務) (メイン3) (業務) (メイン4-1) (メイン4-2) (業務) (サブ2) (業務) (サブ3) (業務) (サブ1-1) (サブ1-2) (サブ1-3) (業務) (サブ4-1) (サブ4-2) (業務) ユーザー 管理 ユーザー管理 ログ 監視 (事業) ↔ : パートナーシップ → → → : 顧客/供給者 → : 順応者(下流側に腐敗防止層を用意) 汎用サブドメイン 支援サブドメイン コアドメイン 境界づけられたコンテキスト
  19. 19. 新システム構成(模式図) UI コア機能 外部連携 RDB キャッシュ キャッシュ キャッシュ 主要機能3 RDB 主要機能2 RDB 主要機能1 RDB サブ機能3 サブ機能2 サブ機能1 サブ機能6 サブ機能5 サブ機能4 RDB キャッシュ キャッシュ キャッシュ RDB キャッシュ サブ機能7 RDB 各機能(コンテキスト)は それぞれDockerコンテナとして動作 (複数配置しALBで分散)し、 REST APIでやりとり :
  20. 20. One more thing...
  21. 21. ユビキタス言語 • コンテキストごとに独自の用語を制定 • システムとしてはコンテキスト間で用語を統一し た方がわかりやすく、同じ用語を別コンテキストで 別の意味で使う、ということはしていない • プログラム上での命名も決めた→これは好評
  22. 22. 3 実装前に 準備したこと
  23. 23. ルール • Gitリポジトリの運用ルール • コーディング規約 • クリーンアーキテクチャでの実装 • パッケージの分け方 • リポジトリの実装 • REST API規約 • REST APIのエラー応答に使用する例外クラス • ログレベル • 利用するサードパーティソフトウェア など
  24. 24. ルール • Gitリポジトリの運用ルール • コーディング規約 • クリーンアーキテクチャでの実装 • パッケージの分け方 • リポジトリの実装 • REST API規約 • REST APIのエラー応答に使用する例外クラス • ログレベル • 利用するサードパーティソフトウェア など 次のページから このあたりを説明
  25. 25. コーディング規約 • 自社コーディング規約に準ずる • Oracle のコード規約 http://www.oracle.com/technetwork/art icles/javase/codeconvtoc-136057.html +独自条項いくつか • ソースコードレビュー観点(「テストにここ出ます」) • 特に重要な4つの心構え「別の人が保守できるの か」「今後の拡張性はあるか」「無駄はないか」「影 響範囲が広すぎないか」をベースに言語共通29項 目+Java固有11項目をWikiに記載
  26. 26. ソースコードレビュー観点(抜粋1) • 基本 • APIの使い方が妥当か(目的に即したAPIをAPI の仕様通りに使用しているか) • 数値でよいところを文字列で処理していないか。 • 適切な型を使うべき • 保守性 • 必要十分なコメントが書けているか。 • 実装の理由や背景をコメントに詳しく書く(コードの内容 そのものよりもコードで表現できないものを残す) • クラスやメソッド・関数のドキュメントもきちんと書く(例外 もぬかりなく)
  27. 27. ソースコードレビュー観点(抜粋2) • 安全性 • 渡されてきた引数は使用する前にバリデーションされ ているか。 • マルチスレッドでアクセスされる変数の値の書き換え はアトミックな代入が保証されているか。 • キャッシュを利用する場合に、作りっぱなしではなくラ イフサイクルが考慮されているか。 • ストリームが閉じられているか。ExceptionやError が起きても大丈夫か。 • メソッドの戻り値が不必要にnullになっていないか? (呼び出し元でnullになりうる意識があるか)
  28. 28. 各コンテキスト内のアーキテクチャ
  29. 29. パッケージ構成 com.justsystems.(サービス名) .(コンテキスト名) .interfaces .controller .presenter .gateway .usecases .domains .model .(集約名) .utils Controller, Translator Presenter, Translator Gateway, Translator Usecase Entity, ValueObject, Service, Repository
  30. 30. 特別なパッケージ • com.justsystems.(サービス名).pl • 公開された言語 (Published Language) • このパッケージ配下に (コンテキスト名) のパッケージを 作り、そのコンテキストの入出力データを定義 • 各コンテキストは呼び出し先の pl 配下のクラスを使用 • com.justsystems.(サービス名).library • ビジネスロジックに全く関与しない、複数コンテキスト間 で共有する価値のあるライブラリ
  31. 31. リポジトリの実装 • RDBへはSpring Data JPA (Hibernate)で アクセス • Domainではインタフェースだけ定義して Gatewayで実処理 Usecase Domain Gateway class Hoge Usecase interface Hoge Repository interface JpaHoge Repository class Hoge Repository Impl
  32. 32. 現在 絶賛コーディング・ テスト中
  33. 33. 4 実装してみて わかったこと
  34. 34. DDD/クリーンアーキテクチャの恩恵 • 関心事に集中できる • @Transactional で考えないといけない範囲が狭 くてイイ! • 用語が統一されているので外部仕様からコードまで すっきりとつながる! • 処理の流れがパターン化されているので追いや すい • 道を外れているのも気づきやすく、コードレビューで フィードバック
  35. 35. 変換の嵐... • ControllerのTranslator • PL→ドメインオブジェクトの変換 • GatewayのTranslator • ドメインオブジェクト→PLの変換 • PL→ドメインオブジェクトの変換 • ドメインオブジェクト→RDB用エンティティの変換 • RDB用エンティティ→ドメインオブジェクトの変換 • PresenterのTranslator • ドメインオブジェクト→PLの変換
  36. 36. 変換の嵐:図にするとこんな感じ Controller Gateway Presenter Translator Translator Translator Translator 他のコンテキストやストレージとの間で変換が走る Domain Object PL PL PL Entity
  37. 37. 変換の嵐:より正確にはこんな感じ 変換が必要なオブジェクトごとにTranslatorを準備 Controller Gateway Presenter Translator Translator Translator Translator Domain Object PL PL PL Entity Translator Translator Translator Translator Translator Translator Translator Translator Translator Translator Translator Translator Translator Translator Translator Translator
  38. 38. 規約で決めたこと以外はみんなバラバラ • REST API通信の実装 • RestTemplate を使用することは決めていた • 各コンテキストでそれぞれ RestTemplate を使った 通信ロジックが生まれそうになった(リトライ処理等) →早くできた人のものを共通ライブラリ化 (library) • サービスのビジネスロジックに全く依存しない部分を基底ク ラスで定義 • 各コンテキストでサブクラスを作成し必要なところだけオー バーライド(通信先のコンフィグなど)
  39. 39. nullチェックの方法もバラバラ • @lombok.NonNull • Objects.requireNonNull(argument) • if (argument == null) { throw new IllegalArgumentException(...); } これは統一をあきらめた (適材適所)
  40. 40. 本当にバラバラ • @lombok.RequiredArgsConstructor vs @lombok.Builder • 中にはコンストラクタも @Builder もあるクラスも… • static な Translator vs DI可能なTranslator • Stream API vs `for` • java.text.SimpleDateFormat vs java.time.format.DateTimeFormatter • これはさすがに DateTimeFormatter にそろえた
  41. 41. 5 やってみてよかった ことと変えたいこと
  42. 42. よかった:モノリスからの解放 • 各担当者が自分の担当部分のビジネスロジック に集中できる • RDB/キャッシュ分離で責任範囲が明確 • 他コンテキストのRDBには一切アクセスできない • データの重複保持もない
  43. 43. よかった:新技術への挑戦 • Docker • AWS ECS • AWS Fargate にしたかったがまだ日本に来ておら ずレイテンシの観点から断念 • GitLab CI and more
  44. 44. 改善の余地あり:ルールの徹底 • キックオフ時に説明会を開催 • こういうアーキテクチャで~ • こういうコーディング規約で~ • 質問は控えめ (ゼロではなかった) • そしてテストに出すといったところがアウト • マージリクエストへのコメントに同じことをよく書いているの でATOKが覚える 規約の浸透方法や内容に課題があるのかもしれない
  45. 45. 6 まとめ
  46. 46. まとめ • 過去の失敗からDDDやクリーンアーキテクチャを 採用するに至った • シナリオ→ロバストネス分析→コンテキストマップ • ユビキタス言語でプログラム上の命名も定義 • 規約・レビュー観点などルールを決めた • 決めていないところはバラバラになった • アーキテクチャをパッケージに落とし込んだ • 新しいことに挑戦できたことはよかった • ルールの徹底方法は改善の余地あり

×