Upgrade to Pro — share decks privately, control downloads, hide ads and more …

マイクロサービスアーキテクチャをあきらめないための、モノリスで始めるアーキテクチャテスト / #jjug_ccc_b #ccc_b8 / JJUG CCC 2020 Fall

Yu Kawanami
November 07, 2020

マイクロサービスアーキテクチャをあきらめないための、モノリスで始めるアーキテクチャテスト / #jjug_ccc_b #ccc_b8 / JJUG CCC 2020 Fall

Yu Kawanami

November 07, 2020
Tweet

More Decks by Yu Kawanami

Other Decks in Technology

Transcript

  1. Microservices architecture を
    あきらめないための、
    Monolith で始める
    アーキテクチャテスト
    JJUG CCC 2020 Fall
    @kawanamiyuu

    View full-size slide

  2. Architecture testing to evolve
    Monolithic architecture
    into the Microservices
    architecture.
    JJUG CCC 2020 Fall
    @kawanamiyuu

    View full-size slide

  3. 自己紹介
    ● かわなみゆう
    ● @kawanamiyuu
    ● 株式会社ラクス / Lead Engineer
    ● 人事・労務業務を楽にする SaaS の開発
    ● Spring Boot / Doma / Vue.js / Puppeteer / GitLab CI
    ● まさかアーキテクチャテストでネタかぶりするとは!
    3

    View full-size slide

  4. 今日話すこと
    1. 新規プロダクト立ち上げ時の、モノリスという選択
    2. モノリスあるあると、アーキテクチャ設計という営みの課題
    3. アーキテクチャテストの紹介
    4. モノリスの発展性を支えるアーキテクチャテスト
    4

    View full-size slide

  5. 新規プロダクト立ち上げ時の、
    モノリスという選択
    5
    Photo by Dan Meyers on Unsplash

    View full-size slide

  6. さあ始めよう!MSA ? モノリス ?
    新しくプロダクトを始めようというときに、クラウドとか MSA とか流
    行りの開発言語とか、いろいろ技術的な夢をみがち
    6

    View full-size slide

  7. 新規プロダクト立ち上げ時に(技術的に)やること
    1. 対象の業務領域(ドメイン)・業務要求の理解、分析
    2. 満たすべき品質特性の特定、優先度付け
    3. 技術スタックの選定
    ○ アーキテクチャ
    ○ ミドルウェア
    ○ 開発言語
    ○ etc
    7

    View full-size slide

  8. アーキテクチャの選択肢
    ● マイクロサービスアーキテクチャ
    ● モノリシックアーキテクチャ
    8

    View full-size slide

  9. 「新規プロダクト立ち上げ」時のアーキテク
    チャ選択における課題感
    9

    View full-size slide

  10. アーキテクチャ選択における課題感
    ● 業務ドメインに対する知識の少なさから、適切な粒度のサービ
    ス分割が難しい
    ● そもそも売れるか分からない、仮説検証を高速にまわしてい
    かなければいけない段階で、MSA で開発・運用するオーバー
    ヘッドが大きい
    ● 手段が目的化している感
    10

    View full-size slide

  11. 11
    https://tech-blog.rakus.co.jp/entry/20180926/microservice

    View full-size slide

  12. アーキテクチャ選択における課題感
    ● 業務ドメインに対する知識の少なさから、適切な粒度のサービ
    ス分割が難しい
    ● そもそも売れるか分からない、仮説検証を高速にまわしてい
    かなければいけない段階で、MSA で開発・運用するオーバー
    ヘッドが大きい
    ● 手段が目的化している感
    12

    View full-size slide

  13. アーキテクチャ選択における課題感
    ● 業務ドメインに対する知識の少なさから、適切な粒度のサービ
    ス分割が難しい
    ● そもそも売れるか分からない、仮説検証を高速にまわしてい
    かなければいけない段階で、MSA で開発・運用するオーバー
    ヘッドが大きい
    ● 手段が目的化している感
    13

    View full-size slide

  14. アーキテクチャ選択における課題感
    ● 業務ドメインに対する知識の少なさから、適切な粒度のサービ
    ス分割が難しい
    ● そもそも売れるか分からない、仮説検証を高速にまわしてい
    かなければいけない段階で、MSA で開発・運用するオーバー
    ヘッドが大きい
    ● 手段が目的化している感
    14

    View full-size slide

  15. アーキテクチャ選択における課題感
    ● 業務ドメインに対する知識の少なさから、適切な粒度のサービ
    ス分割が難しい
    ● そもそも売れるか分からない、仮説検証を高速にまわしてい
    かなければいけない段階で、MSA で開発・運用するオーバー
    ヘッドが大きい
    ● 手段が目的化している感
    15
    このような課題感から現実的な解としての
    モノリシックなアプリケーション開発

    View full-size slide

  16. モノリスあるある、と
    アーキテクチャ設計という営みの課題
    16
    Photo by 贝莉儿 DANIST on Unsplash

    View full-size slide

  17. モノリスあるある
    17

    View full-size slide

  18. モノリスあるある
    ● 適切なモジュール分割を実現するためにドメイン駆動設計や、
    具体的な設計パターンとしてクリーンアーキテクチャやレイ
    ヤードアーキテクチャなどの方法をとることが多い
    ● あとから分割すればよいと開発を始めたモノリスで、いざ分割
    を検討する段階でアプリケーション内の依存関係が複雑に絡
    み合い、分割したくとも解きほぐすのが困難
    18

    View full-size slide

  19. モノリスあるある
    ● 適切なモジュール分割を実現するためにドメイン駆動設計や、
    具体的な設計パターンとしてクリーンアーキテクチャやレイ
    ヤードアーキテクチャなどの方法をとることが多い
    ● あとから分割すればよいと開発を始めたモノリスで、いざ分割
    を検討する段階でアプリケーション内の依存関係が複雑に絡
    み合い、分割したくとも解きほぐすのが困難
    19

    View full-size slide

  20. アーキテクチャ設計という営みの課題
    20

    View full-size slide

  21. こんな悩みありませんか?
    21

    View full-size slide

  22. アーキテクトの悩み
    ● 開発初期に頑張って検討した設計方針が、納期優先・相次ぐ
    メンバー増員により、いつのまにか泥団子に
    ● 開発プロセスとしてコードレビューは機能しているが、アーキテ
    クチャの観点ではレビューされない
    ● 開発メンバーに設計力を上げてもらうためにチャレンジさせた
    いけど、丸投げするのはちょっと不安
    22

    View full-size slide

  23. アーキテクトの悩み
    ● 開発初期に頑張って検討した設計方針が、納期優先・相次ぐ
    メンバー増員により、いつのまにか泥団子に
    ● 開発プロセスとしてコードレビューは機能しているが、アーキテ
    クチャの観点ではレビューされない
    ● 開発メンバーに設計力を上げてもらうためにチャレンジさせた
    いけど、丸投げするのはちょっと不安
    23

    View full-size slide

  24. アーキテクトの悩み
    ● 開発初期に頑張って検討した設計方針が、納期優先・相次ぐ
    メンバー増員により、いつのまにか泥団子に
    ● 開発プロセスとしてコードレビューは機能しているが、アーキテ
    クチャの観点ではレビューされない
    ● 開発メンバーに設計力を上げてもらうためにチャレンジさせた
    いけど、丸投げするのはちょっと不安
    24

    View full-size slide

  25. アーキテクトの悩み
    ● 開発初期に頑張って検討した設計方針が、納期優先・相次ぐ
    メンバー増員により、いつのまにか泥団子に
    ● 開発プロセスとしてコードレビューは機能しているが、アーキテ
    クチャの観点ではレビューされない
    ● 開発メンバーに設計力を上げてもらうためにチャレンジさせた
    いけど、丸投げするのはちょっと不安
    25

    View full-size slide

  26. 開発メンバーの悩み
    ● どのパッケージにクラスを置いたらいいか毎回迷う
    ● コードレビューで指摘されたけど、そんなルール聞いていな
    し、ドキュメントもないので知りようがない
    ● ドメイン駆動設計?Clean Architecture?難しそうだし、ソー
    スコードがどうあればそれらが適用されたアーキテクチャとい
    えるのかイメージできない
    26

    View full-size slide

  27. 開発メンバーの悩み
    ● どのパッケージにクラスを置いたらいいか毎回迷う
    ● コードレビューで指摘されたけど、そんなルール聞いていな
    し、ドキュメントもないので知りようがない
    ● ドメイン駆動設計?Clean Architecture?難しそうだし、ソー
    スコードがどうあればそれらが適用されたアーキテクチャとい
    えるのかイメージできない
    27

    View full-size slide

  28. 開発メンバーの悩み
    ● どのパッケージにクラスを置いたらいいか毎回迷う
    ● コードレビューで指摘されたけど、そんなルール聞いていな
    し、ドキュメントもないので知りようがない
    ● ドメイン駆動設計?Clean Architecture?難しそうだし、ソー
    スコードがどうあればそれらが適用されたアーキテクチャとい
    えるのかイメージできない
    28

    View full-size slide

  29. 開発メンバーの悩み
    ● どのパッケージにクラスを置いたらいいか毎回迷う
    ● コードレビューで指摘されたけど、そんなルール聞いていな
    し、ドキュメントもないので知りようがない
    ● ドメイン駆動設計?Clean Architecture?難しそうだし、ソー
    スコードがどうあればそれらが適用されたアーキテクチャとい
    えるのかイメージできない
    29

    View full-size slide

  30. 悩みの原因
    ● アーキテクチャ設計に関する知識が属人化している
    ● アーキテクチャ設計に関する知識が暗黙知化している
    ● 知っている人が人力でチェックするしかない
    ● 知らなければ当然、チェックされることなくすり抜けてしまう
    30

    View full-size slide

  31. 悩みの原因
    ● アーキテクチャ設計に関する知識が属人化している
    ● アーキテクチャ設計に関する知識が暗黙知化している
    ● 知っている人が人力でチェックするしかない
    ● 知らなければ当然、チェックされることなくすり抜けてしまう
    31
    ソースコードの品質担保以上に、アーキテクチャの品質担保は難しい

    View full-size slide

  32. アーキテクチャの正体
    32

    View full-size slide

  33. Context Map
    33
    https://www.infoq.com/articles/ddd-contextmapping/

    View full-size slide

  34. Context Map
    34
    https://www.infoq.com/articles/ddd-contextmapping/

    View full-size slide

  35. Layered Architecture
    (〇〇〇〇を逆転したレイヤードアーキテクチャ) 35
    (一般的なレイヤードアーキテクチャ)

    View full-size slide

  36. Layered Architecture
    (〇〇〇〇を逆転したレイヤードアーキテクチャ) 36
    (一般的なレイヤードアーキテクチャ)

    View full-size slide

  37. Clean Architecture
    37

    View full-size slide

  38. Clean Architecture
    38

    View full-size slide

  39. Domain Model
    39

    View full-size slide

  40. 依存関係
    40

    View full-size slide

  41. アーキテクチャとは
    「依存関係」のガイドライン
    41

    View full-size slide

  42. アーキテクチャの正体
    ● アーキテクチャとはソフトウェアの構造についての取り決めで
    あり、
    ● 解像度を上げていくと、ソフトウェアを構成する責務や関心事
    の「依存関係」についての取り決め
    ● (乱暴に言うと)Layered Architecture も Clean
    Architecture も 、DDD のような設計論も、依存関係を適切
    に設計したいだけ
    42

    View full-size slide

  43. 「アーキテクチャ」の問題点
    43

    View full-size slide

  44. 「ガイドライン」というものの性質
    ● ガイドラインとは「指針」「ルール」「マナー」
    ● 人が決めて、守る(守らせる)
    ● 最初にルールをつくるのは、簡単
    ● ルール通りつくり始めるのも、簡単
    44

    View full-size slide

  45. 「ガイドライン」というものの性質
    ● ガイドラインとは「指針」「ルール」「マナー」
    ● 人が決めて、守る(守らせる)
    ● 最初にルールをつくるのは、簡単
    ● ルール通りつくり始めるのも、簡単
     なにが難しいのか?
    45

    View full-size slide

  46. 「ガイドライン」というものの性質
    ● ガイドラインとは「指針」「ルール」「マナー」
    ● 人が決めて、守る(守らせる)
    ● 最初にルールをつくるのは、簡単
    ● ルール通りつくり始めるのも、簡単
     なにが難しいのか?
    46
    アーキテクチャの維持が難しい。
    人が決めたものであるがゆえ、壊れやすい。

    View full-size slide

  47. どうすればよいか?
    47

    View full-size slide

  48. 48
    アーキテクチャをテストしたい

    View full-size slide

  49. アーキテクチャテストの紹介
    49
    Photo by ThisisEngineering RAEng on Unsplash

    View full-size slide

  50. 50
    https://www.archunit.org/

    View full-size slide

  51. ArchUnit
    ● GitHub
    ○ https://github.com/TNG/ArchUnit
    ○ https://github.com/TNG/ArchUnit-Examples
    ● Twitter
    ○ https://twitter.com/archtests
    ● Technology Radar (2018)
    ○ https://www.thoughtworks.com/radar/tools/archunit
    ○ 進化的アーキテクチャ x 適応度関数
    51

    View full-size slide

  52. ArchUnit を一言でいうと
    ● Java(や Kotlin, Scala)で書かれたアプリケーションのパッ
    ケージやクラスの依存関係を JUnit のテストコードとして表現
    し、テストできるテストフレームワーク
    ● 自動化により、一定の強制力をもってアーキテクチャの設計品
    質を担保し続けることができる
    ● 依存関係の他にも、そのアプリケーション固有の実装ルール
    をコード化して、テストすることもできる
    52

    View full-size slide

  53. ArchUnit を一言でいうと
    ● Java(や Kotlin, Scala)で書かれたアプリケーションのパッ
    ケージやクラスの依存関係を JUnit のテストコードとして表現
    し、テストできるテストフレームワーク
    ● 自動化により、一定の強制力をもってアーキテクチャの設計品
    質を担保し続けることができる
    ● 依存関係の他にも、そのアプリケーション固有の実装ルール
    をコード化して、テストすることもできる
    53

    View full-size slide

  54. ArchUnit を一言でいうと
    ● Java(や Kotlin, Scala)で書かれたアプリケーションのパッ
    ケージやクラスの依存関係を JUnit のテストコードとして表現
    し、テストできるテストフレームワーク
    ● 自動化により、一定の強制力をもってアーキテクチャの設計品
    質を担保し続けることができる
    ● 依存関係の他にも、そのアプリケーション固有の実装ルール
    をコード化して、テストすることもできる
    54

    View full-size slide

  55. 他のプログラミング言語でのアーキテクチャテスト
    ● TNG/ArchUnitNET(C#)
    ● iternity/archlint.cs(C#)
    ● BenMorris/NetArchTest(.Net)
    ● sensiolabs-de/deptrac(PHP)
    ● carlosas/phpat(PHP)
    ● nazonohito51/dependency-analyzer(PHP)
    55

    View full-size slide

  56. アーキテクチャの典型的なテストの例
    56

    View full-size slide

  57. アーキテクチャの典型的なテスト
    ● レイヤードアーキテクチャ
    ● パッケージ間・クラス間の依存管理
    ● 実行環境・フレームワークへの依存管理
    57

    View full-size slide

  58. アーキテクチャの典型的なテスト
    ● レイヤードアーキテクチャ
    ● パッケージ間・クラス間の依存管理
    ● 実行環境・フレームワークへの依存管理
    58

    View full-size slide

  59. 59
    @Test
    void DIP_依存性逆転の原則_を適用したレイヤードアーキテクチャ () {
    layeredArchitecture()
    .layer("ui").definedBy("com.example.presentation..")
    .layer("app").definedBy("com.example.application..")
    .layer("domain").definedBy("com.example.domain..")
    .layer("infra").definedBy("com.example.infrastructure..")
    .whereLayer("ui").mayOnlyBeAccessedByLayers("infra")
    .whereLayer("app").mayOnlyBeAccessedByLayers("infra", "ui")
    .whereLayer("domain").mayOnlyBeAccessedByLayers("infra", "app")
    .whereLayer("infra").mayNotBeAccessedByAnyLayer()
    .check(CLASSES);
    }

    View full-size slide

  60. 60
    @Test
    void DIP_依存性逆転の原則_を適用したレイヤードアーキテクチャ () {
    layeredArchitecture()
    .layer("ui").definedBy("com.example.presentation..")
    .layer("app").definedBy("com.example.application..")
    .layer("domain").definedBy("com.example.domain..")
    .layer("infra").definedBy("com.example.infrastructure..")
    .whereLayer("ui").mayOnlyBeAccessedByLayers("infra")
    .whereLayer("app").mayOnlyBeAccessedByLayers("infra", "ui")
    .whereLayer("domain").mayOnlyBeAccessedByLayers("infra", "app")
    .whereLayer("infra").mayNotBeAccessedByAnyLayer()
    .check(CLASSES);
    }

    View full-size slide

  61. 61
    @Test
    void DIP_依存性逆転の原則_を適用したレイヤードアーキテクチャ () {
    layeredArchitecture()
    .layer("ui").definedBy("com.example.presentation..")
    .layer("app").definedBy("com.example.application..")
    .layer("domain").definedBy("com.example.domain..")
    .layer("infra").definedBy("com.example.infrastructure..")
    .whereLayer("ui").mayOnlyBeAccessedByLayers("infra")
    .whereLayer("app").mayOnlyBeAccessedByLayers("infra", "ui")
    .whereLayer("domain").mayOnlyBeAccessedByLayers("infra", "app")
    .whereLayer("infra").mayNotBeAccessedByAnyLayer()
    .check(CLASSES);
    }

    View full-size slide

  62. 62
    @Test
    void DIP_依存性逆転の原則_を適用したレイヤードアーキテクチャ () {
    layeredArchitecture()
    .layer("ui").definedBy("com.example.presentation..")
    .layer("app").definedBy("com.example.application..")
    .layer("domain").definedBy("com.example.domain..")
    .layer("infra").definedBy("com.example.infrastructure..")
    .whereLayer("ui").mayOnlyBeAccessedByLayers("infra")
    .whereLayer("app").mayOnlyBeAccessedByLayers("infra", "ui")
    .whereLayer("domain").mayOnlyBeAccessedByLayers("infra", "app")
    .whereLayer("infra").mayNotBeAccessedByAnyLayer()
    .check(CLASSES);
    }

    View full-size slide

  63. 63
    @Test
    void DIP_依存性逆転の原則_を適用したレイヤードアーキテクチャ () {
    layeredArchitecture()
    .layer("ui").definedBy("com.example.presentation..")
    .layer("app").definedBy("com.example.application..")
    .layer("domain").definedBy("com.example.domain..")
    .layer("infra").definedBy("com.example.infrastructure..")
    .whereLayer("ui").mayOnlyBeAccessedByLayers("infra")
    .whereLayer("app").mayOnlyBeAccessedByLayers("infra", "ui")
    .whereLayer("domain").mayOnlyBeAccessedByLayers("infra", "app")
    .whereLayer("infra").mayNotBeAccessedByAnyLayer()
    .check(CLASSES);
    }

    View full-size slide

  64. 64
    @Test
    void DIP_依存性逆転の原則_を適用したレイヤードアーキテクチャ () {
    layeredArchitecture()
    .layer("ui").definedBy("com.example.presentation..")
    .layer("app").definedBy("com.example.application..")
    .layer("domain").definedBy("com.example.domain..")
    .layer("infra").definedBy("com.example.infrastructure..")
    .whereLayer("ui").mayOnlyBeAccessedByLayers("infra")
    .whereLayer("app").mayOnlyBeAccessedByLayers("infra", "ui")
    .whereLayer("domain").mayOnlyBeAccessedByLayers("infra", "app")
    .whereLayer("infra").mayNotBeAccessedByAnyLayer()
    .check(CLASSES);
    }

    View full-size slide

  65. 65
    @Test
    void DIP_依存性逆転の原則_を適用したレイヤードアーキテクチャ () {
    layeredArchitecture()
    .layer("ui").definedBy("com.example.presentation..")
    .layer("app").definedBy("com.example.application..")
    .layer("domain").definedBy("com.example.domain..")
    .layer("infra").definedBy("com.example.infrastructure..")
    .whereLayer("ui").mayOnlyBeAccessedByLayers("infra")
    .whereLayer("app").mayOnlyBeAccessedByLayers("infra", "ui")
    .whereLayer("domain").mayOnlyBeAccessedByLayers("infra", "app")
    .whereLayer("infra").mayNotBeAccessedByAnyLayer()
    .check(CLASSES);
    }

    View full-size slide

  66. 66
    $ ./gradlew clean test
    > Task :test FAILED
    com.example.ArchitectureTest > DIP_依存性逆転の原則_を適用したレイヤーアーキテクチャ
    () FAILED
    java.lang.AssertionError: Architecture Violation [Priority: MEDIUM] - Rule Layered architecture consisting of
    layer 'ui' ('com.example.presentation..')
    layer 'app' ('com.example.application..')
    layer 'domain' ('com.example.domain..')
    layer 'infra' ('com.example.infrastructure..')
    where layer 'ui' may only be accessed by layers ['infra']
    where layer 'app' may only be accessed by layers ['infra', 'ui']
    where layer 'domain' may only be accessed by layers ['infra', 'app']
    where layer 'infra' may not be accessed by any layer was violated (2 times):
    Constructor
    (com.example.infrastructure.datasource.EmployeeRepositoryImpl)> has parameter
    of type in (EmployeeService.java:0)
    Field has type
    in (EmployeeService.java:0)
    at com.tngtech.archunit.lang.ArchRule$Assertions.assertNoViolation(ArchRule.java:91)
    at com.tngtech.archunit.lang.ArchRule$Assertions.check(ArchRule.java:81)
    at com.tngtech.archunit.library.Architectures$LayeredArchitecture.check(Architectures.java:178)
    at com.example.ArchitectureTest.DIP_依存性逆転の原則_を適用したレイヤーアーキテクチャ
    (ArchitectureTest.java:28)

    View full-size slide

  67. 67
    $ ./gradlew clean test
    > Task :test FAILED
    com.example.ArchitectureTest > DIP_依存性逆転の原則_を適用したレイヤーアーキテクチャ
    () FAILED
    java.lang.AssertionError: Architecture Violation [Priority: MEDIUM] - Rule Layered architecture consisting of
    layer 'ui' ('com.example.presentation..')
    layer 'app' ('com.example.application..')
    layer 'domain' ('com.example.domain..')
    layer 'infra' ('com.example.infrastructure..')
    where layer 'ui' may only be accessed by layers ['infra']
    where layer 'app' may only be accessed by layers ['infra', 'ui']
    where layer 'domain' may only be accessed by layers ['infra', 'app']
    where layer 'infra' may not be accessed by any layer was violated (2 times):
    Constructor
    (com.example.infrastructure.datasource.EmployeeRepositoryImpl)> has parameter
    of type in (EmployeeService.java:0)
    Field has type
    in (EmployeeService.java:0)
    at com.tngtech.archunit.lang.ArchRule$Assertions.assertNoViolation(ArchRule.java:91)
    at com.tngtech.archunit.lang.ArchRule$Assertions.check(ArchRule.java:81)
    at com.tngtech.archunit.library.Architectures$LayeredArchitecture.check(Architectures.java:178)
    at com.example.ArchitectureTest.DIP_依存性逆転の原則_を適用したレイヤーアーキテクチャ
    (ArchitectureTest.java:28)

    View full-size slide

  68. 68
    $ ./gradlew clean test
    > Task :test FAILED
    com.example.ArchitectureTest > DIP_依存性逆転の原則_を適用したレイヤーアーキテクチャ
    () FAILED
    java.lang.AssertionError: Architecture Violation [Priority: MEDIUM] - Rule Layered architecture consisting of
    layer 'ui' ('com.example.presentation..')
    layer 'app' ('com.example.application..')
    layer 'domain' ('com.example.domain..')
    layer 'infra' ('com.example.infrastructure..')
    where layer 'ui' may only be accessed by layers ['infra']
    where layer 'app' may only be accessed by layers ['infra', 'ui']
    where layer 'domain' may only be accessed by layers ['infra', 'app']
    where layer 'infra' may not be accessed by any layer was violated (2 times):
    Constructor
    (com.example.infrastructure.datasource.EmployeeRepositoryImpl)> has parameter
    of type in (EmployeeService.java:0)
    Field has type
    in (EmployeeService.java:0)
    at com.tngtech.archunit.lang.ArchRule$Assertions.assertNoViolation(ArchRule.java:91)
    at com.tngtech.archunit.lang.ArchRule$Assertions.check(ArchRule.java:81)
    at com.tngtech.archunit.library.Architectures$LayeredArchitecture.check(Architectures.java:178)
    at com.example.ArchitectureTest.DIP_依存性逆転の原則_を適用したレイヤーアーキテクチャ
    (ArchitectureTest.java:28)

    View full-size slide

  69. 69
    com.example.ArchitectureTest > DIP_依存性逆転の原則_を適用したレイヤーアーキテクチャ() FAILED
    java.lang.AssertionError: Architecture Violation [Priority: MEDIUM] - Rule Layered architecture
    consisting of
    layer 'ui' ('com.example.presentation..')
    layer 'app' ('com.example.application..')
    layer 'domain' ('com.example.domain..')
    layer 'infra' ('com.example.infrastructure..')
    where layer 'ui' may only be accessed by layers ['infra']
    where layer 'app' may only be accessed by layers ['infra', 'ui']
    where layer 'domain' may only be accessed by layers ['infra', 'app']
    where layer 'infra' may not be accessed by any layer was violated (2 times):

    View full-size slide

  70. 70
    com.example.ArchitectureTest > DIP_依存性逆転の原則_を適用したレイヤーアーキテクチャ() FAILED
    java.lang.AssertionError: Architecture Violation [Priority: MEDIUM] - Rule Layered architecture
    consisting of
    layer 'ui' ('com.example.presentation..')
    layer 'app' ('com.example.application..')
    layer 'domain' ('com.example.domain..')
    layer 'infra' ('com.example.infrastructure..')
    where layer 'ui' may only be accessed by layers ['infra']
    where layer 'app' may only be accessed by layers ['infra', 'ui']
    where layer 'domain' may only be accessed by layers ['infra', 'app']
    where layer 'infra' may not be accessed by any layer was violated (2 times):

    View full-size slide

  71. 71
    $ ./gradlew clean test
    > Task :test FAILED
    com.example.ArchitectureTest > DIP_依存性逆転の原則_を適用したレイヤーアーキテクチャ
    () FAILED
    java.lang.AssertionError: Architecture Violation [Priority: MEDIUM] - Rule Layered architecture consisting of
    layer 'ui' ('com.example.presentation..')
    layer 'app' ('com.example.application..')
    layer 'domain' ('com.example.domain..')
    layer 'infra' ('com.example.infrastructure..')
    where layer 'ui' may only be accessed by layers ['infra']
    where layer 'app' may only be accessed by layers ['infra', 'ui']
    where layer 'domain' may only be accessed by layers ['infra', 'app']
    where layer 'infra' may not be accessed by any layer was violated (2 times):
    Constructor
    (com.example.infrastructure.datasource.EmployeeRepositoryImpl)> has parameter
    of type in (EmployeeService.java:0)
    Field has type
    in (EmployeeService.java:0)
    at com.tngtech.archunit.lang.ArchRule$Assertions.assertNoViolation(ArchRule.java:91)
    at com.tngtech.archunit.lang.ArchRule$Assertions.check(ArchRule.java:81)
    at com.tngtech.archunit.library.Architectures$LayeredArchitecture.check(Architectures.java:178)
    at com.example.ArchitectureTest.DIP_依存性逆転の原則_を適用したレイヤーアーキテクチャ
    (ArchitectureTest.java:28)

    View full-size slide

  72. 72
    Constructor
    (com.example.infrastructure.datasource.EmployeeRepositor
    yImpl)> has parameter of type in
    (EmployeeService.java:0)
    Field has type
    in (EmployeeService.java:0)
    at com.tngtech.archunit.lang.ArchRule$Assertions.assertNoViolation(ArchRule.java:91)
    at com.tngtech.archunit.lang.ArchRule$Assertions.check(ArchRule.java:81)
    at com.tngtech.archunit.library.Architectures$LayeredArchitecture.check(Architectures.java:178)
    at com.example.ArchitectureTest.DIP_依存性逆転の原則_を適用したレイヤーアーキテクチャ
    (ArchitectureTest.java:28)

    View full-size slide

  73. 73
    Constructor
    (com.example.infrastructure.datasource.EmployeeRepositor
    yImpl)> has parameter of type in
    (EmployeeService.java:0)
    Field has type
    in (EmployeeService.java:0)
    at com.tngtech.archunit.lang.ArchRule$Assertions.assertNoViolation(ArchRule.java:91)
    at com.tngtech.archunit.lang.ArchRule$Assertions.check(ArchRule.java:81)
    at com.tngtech.archunit.library.Architectures$LayeredArchitecture.check(Architectures.java:178)
    at com.example.ArchitectureTest.DIP_依存性逆転の原則_を適用したレイヤーアーキテクチャ
    (ArchitectureTest.java:28)

    View full-size slide

  74. 74
    Constructor
    (com.example.infrastructure.datasource.EmployeeRepositor
    yImpl)> has parameter of type in
    (EmployeeService.java:0)
    Field has type
    in (EmployeeService.java:0)
    at com.tngtech.archunit.lang.ArchRule$Assertions.assertNoViolation(ArchRule.java:91)
    at com.tngtech.archunit.lang.ArchRule$Assertions.check(ArchRule.java:81)
    at com.tngtech.archunit.library.Architectures$LayeredArchitecture.check(Architectures.java:178)
    at com.example.ArchitectureTest.DIP_依存性逆転の原則_を適用したレイヤーアーキテクチャ
    (ArchitectureTest.java:28)

    View full-size slide

  75. 75
    Constructor
    (com.example.infrastructure.datasource.EmployeeRepositor
    yImpl)> has parameter of type in
    (EmployeeService.java:0)
    Field has type
    in (EmployeeService.java:0)
    at com.tngtech.archunit.lang.ArchRule$Assertions.assertNoViolation(ArchRule.java:91)
    at com.tngtech.archunit.lang.ArchRule$Assertions.check(ArchRule.java:81)
    at com.tngtech.archunit.library.Architectures$LayeredArchitecture.check(Architectures.java:178)
    at com.example.ArchitectureTest.DIP_依存性逆転の原則_を適用したレイヤーアーキテクチャ
    (ArchitectureTest.java:28)

    View full-size slide

  76. アーキテクチャの典型的なテスト
    ● レイヤードアーキテクチャ
    ● パッケージ間・クラス間の依存管理
    ● 実行環境・フレームワークへの依存管理
    76

    View full-size slide

  77. 77
    @Test
    void UI層のクラスはインフラストラクチャ層のクラスからのみ依存される() {
    classes().that().resideInAPackage("com.example.presentation..")
    .should()
    .onlyHaveDependentClassesThat().resideInAPackage("com.example.infrastructure..")
    .check(CLASSES);
    }
    @Test
    void ドメイン層のクラスは他の層のクラスに依存しない() {
    noClasses().that().resideInAPackage("com.example.domain..")
    .should()
    .dependOnClassesThat().resideInAnyPackage(
    "com.example.presentation..", "com.example.application..", "com.example.infrastructure..")
    .check(CLASSES);
    }

    View full-size slide

  78. 78
    @Test
    void UI層のクラスはインフラストラクチャ層のクラスからのみ依存される() {
    classes().that().resideInAPackage("com.example.presentation..")
    .should()
    .onlyHaveDependentClassesThat().resideInAPackage("com.example.infrastructure..")
    .check(CLASSES);
    }
    @Test
    void ドメイン層のクラスは他の層のクラスに依存しない() {
    noClasses().that().resideInAPackage("com.example.domain..")
    .should()
    .dependOnClassesThat().resideInAnyPackage(
    "com.example.presentation..", "com.example.application..", "com.example.infrastructure..")
    .check(CLASSES);
    }

    View full-size slide

  79. 79
    @Test
    void UI層のクラスはインフラストラクチャ層のクラスからのみ依存される() {
    classes().that().resideInAPackage("com.example.presentation..")
    .should()
    .onlyHaveDependentClassesThat().resideInAPackage("com.example.infrastructure..")
    .check(CLASSES);
    }
    @Test
    void ドメイン層のクラスは他の層のクラスに依存しない() {
    noClasses().that().resideInAPackage("com.example.domain..")
    .should()
    .dependOnClassesThat().resideInAnyPackage(
    "com.example.presentation..", "com.example.application..", "com.example.infrastructure..")
    .check(CLASSES);
    }

    View full-size slide

  80. 80
    $ ./gradlew clean test
    > Task :test FAILED
    com.example.ArchitectureTest > ドメイン層のクラスは他の層のクラスに依存しない() FAILED
    java.lang.AssertionError: Architecture Violation [Priority: MEDIUM] - Rule 'no classes that reside in a
    package 'com.example.domain..' should depend on classes that reside in any package
    ['com.example.presentation..', 'com.example.application..', 'com.example.infrastructure..']' was violated
    (2 times):
    Constructor
    (com.example.infrastructure.datasource.EmployeeRepositor
    yImpl)> has parameter of type in
    (EmployeeService.java:0)
    Field has type
    in (EmployeeService.java:0)
    at com.tngtech.archunit.lang.ArchRule$Assertions.assertNoViolation(ArchRule.java:91)
    at com.tngtech.archunit.lang.ArchRule$Assertions.check(ArchRule.java:81)
    at com.tngtech.archunit.lang.ArchRule$Factory$SimpleArchRule.check(ArchRule.java:195)
    at com.tngtech.archunit.lang.syntax.ObjectsShouldInternal.check(ObjectsShouldInternal.java:81)
    at com.example.ArchitectureTest.ドメイン層のクラスは他の層のクラスに依存しない(ArchitectureTest.java:38)

    View full-size slide

  81. 81
    $ ./gradlew clean test
    > Task :test FAILED
    com.example.ArchitectureTest > ドメイン層のクラスは他の層のクラスに依存しない() FAILED
    java.lang.AssertionError: Architecture Violation [Priority: MEDIUM] - Rule 'no classes that reside in a
    package 'com.example.domain..' should depend on classes that reside in any package
    ['com.example.presentation..', 'com.example.application..', 'com.example.infrastructure..']' was violated
    (2 times):
    Constructor
    (com.example.infrastructure.datasource.EmployeeRepositor
    yImpl)> has parameter of type in
    (EmployeeService.java:0)
    Field has type
    in (EmployeeService.java:0)
    at com.tngtech.archunit.lang.ArchRule$Assertions.assertNoViolation(ArchRule.java:91)
    at com.tngtech.archunit.lang.ArchRule$Assertions.check(ArchRule.java:81)
    at com.tngtech.archunit.lang.ArchRule$Factory$SimpleArchRule.check(ArchRule.java:195)
    at com.tngtech.archunit.lang.syntax.ObjectsShouldInternal.check(ObjectsShouldInternal.java:81)
    at com.example.ArchitectureTest.ドメイン層のクラスは他の層のクラスに依存しない(ArchitectureTest.java:38)

    View full-size slide

  82. 82
    $ ./gradlew clean test
    > Task :test FAILED
    com.example.ArchitectureTest > ドメイン層のクラスは他の層のクラスに依存しない() FAILED
    java.lang.AssertionError: Architecture Violation [Priority: MEDIUM] - Rule 'no classes that reside in a
    package 'com.example.domain..' should depend on classes that reside in any package
    ['com.example.presentation..', 'com.example.application..', 'com.example.infrastructure..']' was violated
    (2 times):
    Constructor
    (com.example.infrastructure.datasource.EmployeeRepositor
    yImpl)> has parameter of type in
    (EmployeeService.java:0)
    Field has type
    in (EmployeeService.java:0)
    at com.tngtech.archunit.lang.ArchRule$Assertions.assertNoViolation(ArchRule.java:91)
    at com.tngtech.archunit.lang.ArchRule$Assertions.check(ArchRule.java:81)
    at com.tngtech.archunit.lang.ArchRule$Factory$SimpleArchRule.check(ArchRule.java:195)
    at com.tngtech.archunit.lang.syntax.ObjectsShouldInternal.check(ObjectsShouldInternal.java:81)
    at com.example.ArchitectureTest.ドメイン層のクラスは他の層のクラスに依存しない(ArchitectureTest.java:38)

    View full-size slide

  83. 83
    $ ./gradlew clean test
    > Task :test FAILED
    com.example.ArchitectureTest > ドメイン層のクラスは他の層のクラスに依存しない() FAILED
    java.lang.AssertionError: Architecture Violation [Priority: MEDIUM] - Rule 'no classes that reside in a
    package 'com.example.domain..' should depend on classes that reside in any package
    ['com.example.presentation..', 'com.example.application..', 'com.example.infrastructure..']' was violated
    (2 times):
    Constructor
    (com.example.infrastructure.datasource.EmployeeRepositor
    yImpl)> has parameter of type in
    (EmployeeService.java:0)
    Field has type
    in (EmployeeService.java:0)
    at com.tngtech.archunit.lang.ArchRule$Assertions.assertNoViolation(ArchRule.java:91)
    at com.tngtech.archunit.lang.ArchRule$Assertions.check(ArchRule.java:81)
    at com.tngtech.archunit.lang.ArchRule$Factory$SimpleArchRule.check(ArchRule.java:195)
    at com.tngtech.archunit.lang.syntax.ObjectsShouldInternal.check(ObjectsShouldInternal.java:81)
    at com.example.ArchitectureTest.ドメイン層のクラスは他の層のクラスに依存しない(ArchitectureTest.java:38)

    View full-size slide

  84. アーキテクチャの典型的なテスト
    ● レイヤードアーキテクチャ
    ● パッケージ間・クラス間の依存管理
    ● 実行環境・フレームワークへの依存管理
    84

    View full-size slide

  85. 85
    @Test
    void ドメイン層はWeb実行環境に依存しない () {
    noClasses().that().resideInAPackage("com.example.domain..")
    .should()
    .dependOnClassesThat().resideInAPackage("javax.servlet..")
    .check(CLASSES);
    }
    @Test
    void ドメイン層はWebアプリケーションフレームワークに依存しない () {
    noClasses().that().resideInAPackage("com.example.domain..")
    .should()
    .dependOnClassesThat().resideInAPackage("org.springframework..")
    .check(CLASSES);
    }

    View full-size slide

  86. 86
    @Test
    void ドメイン層はWeb実行環境に依存しない () {
    noClasses().that().resideInAPackage("com.example.domain..")
    .should()
    .dependOnClassesThat().resideInAPackage("javax.servlet..")
    .check(CLASSES);
    }
    @Test
    void ドメイン層はWebアプリケーションフレームワークに依存しない () {
    noClasses().that().resideInAPackage("com.example.domain..")
    .should()
    .dependOnClassesThat().resideInAPackage("org.springframework..")
    .check(CLASSES);
    }

    View full-size slide

  87. モノリスの発展性を支える
    アーキテクチャテスト
    87
    Photo by Markus Spiske on Unsplash

    View full-size slide

  88. アーキテクチャの視座
    ● モノリス内に存在する責務や関心事を適切な粒度で切り分け

    ○ 大きな粒度:レイヤー、ドメイン
    ○ 小さな粒度:パッケージ、クラス
    ● それぞれの粒度で依存関係を注意深く設計する
    88

    View full-size slide

  89. アーキテクチャの視座
    ● モノリス内に存在する責務や関心事を適切な粒度で切り分け

    ○ 大きな粒度:レイヤー、ドメイン
    ○ 小さな粒度:パッケージ、クラス
    ● それぞれの粒度で依存関係を注意深く設計する
    ○ モノリスを将来、マイクロサービスに分割するという観点では、ドメイン
    間の依存関係(=実体としてはパッケージ間の依存関係)にはとくに注
    目 89

    View full-size slide

  90. 依存関係を守り、育てる
    ● 「守る」手段としてのアーキテクチャテスト
    ● 「育てる」手段としてのアーキテクチャテスト
    90

    View full-size slide

  91. 「守る」手段としてのアーキテクチャテスト
    ● Layered Architecture のアーキテクチャテスト
    ● Onion Architecture のアーキテクチャテスト
    ● (Clean Architecture のアーキテクチャテスト)
    ● 実行環境やフレームワークへの依存管理
    ● ドメイン間(パッケージ間)の依存管理
    91

    View full-size slide

  92. 「育てる」手段としてのアーキテクチャテスト?
    92

    View full-size slide

  93. アーキテクチャテストの失敗は悪か?
    ● アーキテクチャテストはあるべき依存関係を表現したもの
    ● アーキテクチャテストは失敗しないはずのテスト
    ● アーキテクチャテストが失敗するとき
    ○ 実装誤り
    ○  

    93

    View full-size slide

  94. アーキテクチャテストの失敗は悪か?
    ● アーキテクチャテストはあるべき依存関係を表現したもの
    ● アーキテクチャテストは失敗しないはずのテスト
    ● アーキテクチャテストが失敗するとき
    ○ 実装誤り
    ○ アーキテクチャに対する発見のサイン
    ○ アーキテクチャについて議論する始点
    94

    View full-size slide

  95. 「育てる」手段としてのアーキテクチャテスト
    ● プロダクト立ち上げ時に、MSA の適切なサービス分割がわか
    らないのと同じく、モノリスにおけるモジュール分割も最初から
    うまくいくわけではない
    ● ソフトウェアの成長の過程で、アーキテクチャに対してもフィー
    ドバックを得て、適切な依存関係を発見していく必要がある
    95

    View full-size slide

  96. 「育てる」手段としてのアーキテクチャテスト
    ● プロダクト立ち上げ時に、MSA の適切なサービス分割がわか
    らないのと同じく、モノリスにおけるモジュール分割も最初から
    うまくいくわけではない
    ● ソフトウェアの成長の過程で、アーキテクチャに対してもフィー
    ドバックを得て、適切な依存関係を発見していく必要がある
    96

    View full-size slide

  97. アーキテクチャの目的
    98

    View full-size slide

  98. 99
    https://www.infoq.com/articles/ddd-contextmapping/

    View full-size slide

  99. この図は
    ● モノリシックなアプリケーション内部の、ドメイン同士の依存関
    係?
    ● マイクロサービスアーキテクチャで実現されたアプリケーション
    の、サービス同士の依存関係?
    100

    View full-size slide

  100. この図は
    ● モノリシックなアプリケーション内部の、ドメイン同士の依存関
    係?
    ● マイクロサービスアーキテクチャで実現されたアプリケーション
    の、サービス同士の依存関係?
    どちらもありえる。
    101

    View full-size slide

  101. ソフトウェアが「ソフト」であるための選択肢を残す。
    それがアーキテクチャ。
    そのアーキテクチャが意図する依存関係を維持し、ソフトウェアの
    発展の可能性を支えるアーキテクチャテスト。
    102

    View full-size slide