Slide 1

Slide 1 text

ᙱ͍ͱ͜ΖʹखΛಧ͔ͤΔ
 "SDI6OJUͷར༻๏
 ʙΞʔΩςΫνϟʔςετ͔ΒΞϓϦέʔγϣϯղੳ·Ͱʙ JJUG CCC 2020 Fall @ 2020/11/07
 Yuji Koyano

Slide 2

Slide 2 text

ykoyano Yuji Koyano @bandwagondagon w גࣜձࣾϦΫϧʔτͷΤϯδχΞ w ࣾ಺ԣஅσʔλج൫૊৫Ͱͷ։ൃ΍
 ৽نࣄۀͷ"OESPJEΞϓϦ։ൃΛܦͯ
 ݱࡏ͸ϗοτϖούʔϏϡʔςΟʔͷ
 αʔόʔαΠυΤϯδχΞΛ୲౰ 2

Slide 3

Slide 3 text

ϗοτϖούʔϏϡʔςΟʔͱ͸ 3 ࠃ಺࠷େڃͷϔΞαϩϯɾϦϥΫϏϡʔςΟαϩϯͷݕࡧɾ༧໿αʔϏε
 ೥ؒ໿ԯສճͷ༧໿͕ߦΘΕΔʢ࣌఺ʣ IUUQTCFBVUZIPUQFQQFSKQEPDHVJEFTBJTIJOEBUBIUNM

Slide 4

Slide 4 text

͜ͷηογϣϯͷ໨త w ϗοτϖούʔϏϡʔςΟʔͷϞόΠϧΞϓϦ޲͚"1*ͱ
 "OESPJEΞϓϦʹ͓͚Δ"SDI6OJUͷར༻ࣄྫΛ঺հ
 ࢼߦࡨޡதͷ΋ͷ΋ؚΈ·͢ w "SDI6OJUΛར༻ͨ͠ΞʔΩςΫνϟͷ୯ମςετ͔Β
 ੩తղੳΛར༻ͨ͠ΞϓϦέʔγϣϯղੳ·Ͱ
 ༷ʑͳ"SDI6OJUͷ࢖͍ํΛ஌ͬͯ΋Β͏ w ͔Β࣮૷͠Α͏ͱ͢Δͱେม͚ͩͲɺ΋͠Ͱ͖ͨΒخ͍ࣗ͠ಈԽΛ "SDI6OJUΛ࢖࣮ͬͯફ͍ͨ͠ͱࢥͬͯ΋Β͏

Slide 5

Slide 5 text

ΞʔΩςΫνϟʔςετͷར༻๏ "SDI6OJUΛಋೖͨ͠ϓϩμΫτͷഎܠ ΞϓϦέʔγϣϯղੳ΁ͷར༻๏ "SDI6OJUͷ࢓૊Έͱ"1* "SDI6OJUͷجຊతͳػೳ

Slide 6

Slide 6 text

ΞʔΩςΫνϟʔςετͷར༻๏ "SDI6OJUΛಋೖͨ͠ϓϩμΫτͷഎܠ ΞϓϦέʔγϣϯղੳ΁ͷར༻๏ "SDI6OJUͷ࢓૊Έͱ"1* "SDI6OJUͷجຊతͳػೳ

Slide 7

Slide 7 text

w 5/(5FDIOPMPHZ$POTVMUJOH(NC)ࣾʹ
 Αͬͯ։ൃ͞Ε͍ͯΔ044ϥΠϒϥϦ w ΞϓϦέʔγϣϯͷΞʔΩςΫνϟʔ੍໿Λ
 6OJU5FTUͱ࣮ͯ͠૷͢Δ͜ͱͰ
 ΞʔΩςΫνϟʔͷςετΛ࣮ݱ͢Δ w +BWB͚ͩͰ͸ͳ͘,PUMJOͳͲͷ+7.ݴޠͷ
 ΞϓϦέʔγϣϯͰ΋ར༻͕Մೳ 7 ArchUnitͱ͸ IUUQTXXXBSDIVOJUPSH

Slide 8

Slide 8 text

8 IUUQTXXXBSDIVOJUPSHVTFSHVJEFIUNM@*OEFYIUNM JUnitͱͷ࿈ܞ @RunWith(ArchUnitRunner.class) // JUnit5Ͱ͸ෆཁ @AnalyzeClasses(packages = "com.myapp") public class ArchitectureTest { @ArchTest public static final ArchRule rule1 = classes()
 .should()... @ArchTest public static final ArchRule rule2 = classes() .should()... @ArchTest public static void rule3(JavaClasses classes) { // } } w +6OJUͱ࿈ܞͤ͞Δ͜ͱͰ
 "SVD6OJUͰ࣮૷੍ͨ͠໿Λ
 ͦͷ··ςετϝιου
 ͱͯ͠ఆٛͰ͖Δ w ଞͷςετϑϨʔϜϫʔΫʹ
 ૊ΈࠐΉ͜ͱ΋Մೳ

Slide 9

Slide 9 text

9 classes() .that() .resideInAPackage("..foo..") .should() .onlyHaveDependentClassesThat() .resideInAnyPackage( "..source.one..", "..foo.." ); ੍໿ΛJavaίʔυͷUTͰ࣮૷Ͱ͖Δ IUUQTXXXBSDIVOJUPSHVTFSHVJEFIUNM@*OEFYIUNM

Slide 10

Slide 10 text

10 classes() .that() .haveNameMatching(".*Bar") .should() .onlyBeAccessed() .byClassesThat() .haveSimpleName("Bar"); Ϋϥεͷґଘؔ܎ͷ੍໿ςετ IUUQTXXXBSDIVOJUPSHVTFSHVJEFIUNM@*OEFYIUNM

Slide 11

Slide 11 text

11 classes() .that() .implement(Connection.class) .should() .haveSimpleNameEndingWith(
 "Connection"
 ); ໋໊ن໿ͷ੍໿ςετ IUUQTXXXBSDIVOJUPSHVTFSHVJEFIUNM@*OEFYIUNM

Slide 12

Slide 12 text

12 IUUQTXXXBSDIVOJUPSHVTFSHVJEFIUNM@*OEFYIUNM ஈ֊తͳςετͷಋೖ w ͋Δ࣌఺ͷΞʔΩςΫνϟʔςετͷ
 ࣮ߦ݁ՌͷεφοϓγϣοτΛར༻ͯ͠
 ஈ֊తͳςετͷಋೖ͕Մೳ w Ұ෦ͷίʔυͷ੍໿ҧ൓Λແࢹͯ͠
 ৽ࠩ͘͠෼͕͋ͬͨίʔυʹର͚ͯͩ͠ ΞʔΩςΫνϟʔͷ੍໿Λڧ੍Ͱ͖Δ ArchRule rule = FreezingArchRule .freeze( classes().should()... );


Slide 13

Slide 13 text

ಋೖϢʔεέʔεྫ w ґଘؔ܎੍໿ɾ໋໊نଇͳͲΛςετͱ࣮ͯ͠૷ͯ͠ڧ੍͢Δ
 ΞʔΩςΫνϟʔϨϏϡʔͷࣗಈԽ
 ಛఆͷ࣮૷ϧʔϧͷ࿙ΕΛ๷͙
 ςετͱͯ͠ͷυΩϡϝϯςʔγϣϯʹΑΔΞʔΩςΫνϟʔͷ໌จԽ w ஈ֊తͳΞʔΩςΫνϟʔҠߦɾ੔උͷαϙʔτ
 ϦϑΝΫλϦϯάɾϦΞʔΩςΟϯάର৅ͷίʔυʹ෦෼తʹ65Λద༻ w ΞϓϦέʔγϣϯղੳͷϑϨʔϜϫʔΫͱͯ͠ར༻
 ґଘؔ܎ʹؔ͢Δ৘ใͷυΩϡϝϯτͷࣗಈੜ੒ 13

Slide 14

Slide 14 text

ΞʔΩςΫνϟʔςετͷར༻๏ "SDI6OJUΛಋೖͨ͠ϓϩμΫτͷഎܠ ΞϓϦέʔγϣϯղੳ΁ͷར༻๏ "SDI6OJUͷ࢓૊Έͱ"1* "SDI6OJUͷجຊతͳػೳ

Slide 15

Slide 15 text

$PSF"1* -BOH"1* -JCSBSZ"1* $MBTTϑΝΠϧͷCZUFDPEFΛղੳͯ͠Ϋϥε΍
 ϝιουؒͷґଘؔ܎ͳͲϓϦϛςΟϒͳ৘ใΛఏڙ $PSF"1*͔ΒऔಘͰ͖Δ৘ใΛݩʹґଘؔ܎ͷ
 ੍໿Λॊೈ͔ͭ؆ܿʹ࣮૷͢ΔͨΊʹ"1*Λఏڙ -BZFSFEɾ0OJPOΞʔΩςΫνϟʔͳͲయܕతͳ
 ΞʔΩςΫνϟʔͷґଘؔ܎ϧʔϧηοτΛఏڙ 15 ArchUnit͕ఏڙ͢ΔAPI

Slide 16

Slide 16 text

16 // ΫϥεϑΝΠϧ͔ΒJavaClassesΠϯελϯεΛऔಘ JavaClasses importedClasses = new ClassFileImporter() // ςετίʔυͷbytecode͸আ֎͢Δ .withImportOption( ImportOption.d.DO_NOT_INCLUDE_TESTS ) // Ϋϥεύε্ͷbytecodeΛಡΈࠐΉ .importClasspath(); Core APIͱbytecode w $MBTT'JMF*NQPSUFSʹΑͬͯ
 ࢦఆͨ͠ΫϥεϑΝΠϧͷ CZUFDPEFΛಡΈࠐΉ w "4.ϥΠϒϥϦΛར༻ͯ͠
 CZUFDPEFΛղੳͯ͠
 "SDI6OJUݻ༗ͷදݱʹม׵͠
 $PSF"1*Λհͯ͠։ൃऀʹ
 ৘ใΛఏڙ

Slide 17

Slide 17 text

w +BWBόΠτίʔυͷಡΈऔΓɾ
 ॻ͖ࠐΈɾૢ࡞Λߦ͏ϑϨʔϜϫʔΫ w (SBEMF΍,PUMJO$PNQJMFSΛؚΉ
 ଟ͘ͷϓϩδΣΫτͰ࢖༻͞Ε͍ͯΔ w +%,಺෦Ͱ΋ϥϜμࣜͷ࣮ߦ࣌αϙʔτΛ
 ࣮ݱ͢ΔͨΊʹར༻ IUUQTCMPHTPSBDMFDPNKBWBNBHB[JOFSFBMXPSMECZUFDPEFIBOEMJOHXJUIBTN ASMϥΠϒϥϦ

Slide 18

Slide 18 text

18 w +BWB3FqFDUJPO"1*Λ
 ֦ுͨ͠Α͏ͳ"1* w Ϋϥεͷ৘ใʹ3FqFDUJPO"1*
 ΑΓ΋ൺֱతѻ͍΍͍͢ܗͰ
 ΞΫηεͰ͖Δ
 FY0QUJPOBMɾίϨΫγϣϯ"1* IUUQTXXXBSDIVOJUPSHVTFSHVJEFIUNM@*OEFYIUNM Core APIͱRef ection API l

Slide 19

Slide 19 text

19 w Πϯϙʔτͨ͠CZUFDPEF্ͷ
 ΫϥεɾϝιουɾϑΟʔϧυͳͲͷཁૉؒͷ
 ΞΫηεΛશͯه࿥͠ґଘؔ܎άϥϑΛߏங w ґଘཁૉʹΞΫηε͢ΔͨΊͷ"1*Λఏڙ
 FYHFU'JFME"DDFTTFT'SPN4FMG 
 ϑΟʔϧυʹΞΫηε͍ͯ͠ΔཁૉΛऔಘ
 HFU.FUIPE$BMMT5P4FMG 
 ϝιου͕಺෦Ͱݺͼग़͍ͯ͠ΔϝιουΛऔಘ IUUQTXXXBSDIVOJUPSHVTFSHVJEFIUNM@*OEFYIUNM Core APIͱґଘؔ܎

Slide 20

Slide 20 text

20 // serviceͱ͍͏จࣈΛؚΉpackage഑ԼͷΫϥε͸
 // controllerͱ͍͏จࣈΛؚΉpackage഑ԼͷΫϥεʹ
 // ΞΫηεͯ͠͸͍͚ͳ͍
 ArchRule rule = ArchRuleDefinition
 .noClasses() .that()
 .resideInAPackage("..service..") .should()
 .accessClassesThat()
 .resideInAPackage("..controller.."); 
 rule.check(importedClasses);
 Lang API w $PSF"1*͔ΒऔಘͰ͖Δ৘ใΛ ݩʹґଘؔ܎ͷ੍໿Λॊೈ͔ͭ ؆ܿʹ࣮૷͢ΔͨΊͷ"1* w ੍໿ʹҧ൓͢ΔΫϥεͳͲ͕
 ଘࡏͨ͠৔߹ʹςετΛ
 ࣦഊͤ͞Δ͜ͱ͕Ͱ͖Δ w جຊతʹ͸͜ͷ"1*Λ࢖ͬͯ
 ςετΛ࣮૷͢Δ͜ͱ͕ଟ͍

Slide 21

Slide 21 text

21 // ݕࠪର৅ͷΫϥε͕@PayloadΞϊςʔγϣϯ͕෇༩͞Εͨ
 // ϑΟʔϧυΛ͍࣋ͬͯΔ͔ΛνΣοΫ͢ΔΧελϜϧʔϧ
 DescribedPredicate
 haveAFieldAnnotatedWithPayload = new DescribedPredicate<>( "have a field annotated with @Payload” ) { @Override public boolean apply(JavaClass input) { return checkCustomRule(input); } }; Lang APIͱΧελϜϧʔϧ w %FTDSJCFE1SFEJDBUFͳͲͷ
 ΠϯλʔϑΣʔεΛ࣮૷ͯ͠
 ಠࣗͷΧελϜϧʔϧΛ
 ࡞੒͢Δ͜ͱ͕Մೳ w ࣮ݱ͍ͨ͠ϧʔϧʹରԠ͢Δ
 "1*͕ଘࡏ͠ͳ͍৔߹͸
 -BOH"1*ͱ$PSF"1*Λ
 ૊Έ߹Θ੍ͤͯ໿Λ࣮૷͢Δ

Slide 22

Slide 22 text

22 Library API w ࢦఆͨ͠ύοέʔδΛ
 άϧʔϐϯάͯ͠యܕతͳ
 ΞʔΩςΫνϟʔͷ
 ϨΠϠʔͱඥ෇͚Δ
 "1*Λఏڙ w ϨΠϠʔͷґଘؔ܎੍໿Λ
 ͦͷ··ΞϓϦέʔγϣϯʹ
 ద༻͢Δ͜ͱ͕Ͱ͖Δ layeredArchitecture() .layer("Controller").definedBy("..controller..") .layer("Service").definedBy("..service..") .layer("Persistence").definedBy("..persistence..") .whereLayer("Controller").mayNotBeAccessedByAnyLayer() .whereLayer("Service").mayOnlyBeAccessedByLayers("Controller") .whereLayer("Persistence").mayOnlyBeAccessedByLayers("Service");

Slide 23

Slide 23 text

23 Library API layeredArchitecture() .layer("Controller").definedBy("..controller..") .layer("Service").definedBy("..service..") .layer("Persistence").definedBy("..persistence..") .whereLayer("Controller").mayNotBeAccessedByAnyLayer() .whereLayer("Service").mayOnlyBeAccessedByLayers("Controller") .whereLayer("Persistence").mayOnlyBeAccessedByLayers("Service");

Slide 24

Slide 24 text

w "SDI6OJUͷಛ௃
 ઃఆϑΝΠϧ΍%4-Ͱ͸ͳ͘ϥΠϒϥϦ͕ఏڙ͢Δ"1*Λ༻͍ͨ
 +BWBίʔυͰ+6OJUͳͲͷ୯ମςετͱͯ͠هड़Ͱ͖Δ w ྨࣅͷ͜ͱ͕Ұ෦࣮ݱͰ͖Δπʔϧ
 ੍໿ݕࠪɿ"TQFDU+ $IFDLTUZMF 'JOE#VHT
 ґଘؔ܎ɿK2"TTJTUBOU %FQFOE %FHSBQI %FQUFDUJWF ArchUnitͷಛ௃ͱྨࣅπʔϧͱͷൺֱ 24

Slide 25

Slide 25 text

w "SDI6OJUͷಛ௃
 ઃఆϑΝΠϧ΍%4-Ͱ͸ͳ͘ϥΠϒϥϦ͕ఏڙ͢Δ"1*Λ༻͍ͨ
 +BWBίʔυͰ+6OJUͳͲͷ୯ମςετͱͯ͠هड़Ͱ͖Δ w ྨࣅͷ͜ͱ͕Ұ෦࣮ݱͰ͖Δπʔϧ
 ੍໿ݕࠪɿ"TQFDU+ $IFDLTUZMF 'JOE#VHT
 ґଘؔ܎ɿK2"TTJTUBOU %FQFOE %FHSBQI %FQUFDUJWF ArchUnitͷಛ௃ͱྨࣅπʔϧͱͷൺֱ 25 
 
 "SDI6OJUͷศརͦ͏ͳ͜ͱ͸Θ͔Δɻ
 ͚ͩͲɺ࣮ࡍʹͲ͏͍͏࢖͍ํΛͯ͠
 ΞϓϦέʔγϣϯʹಋೖ͍ͯ͠Δͷ͔ʁ Α͋͘Δٙ໰

Slide 26

Slide 26 text

ΞʔΩςΫνϟʔςετͷར༻๏ "SDI6OJUΛಋೖͨ͠ϓϩμΫτͷഎܠ ΞϓϦέʔγϣϯղੳ΁ͷར༻๏ "SDI6OJUͷ࢓૊Έͱ"1* "SDI6OJUͷجຊతͳػೳ

Slide 27

Slide 27 text

w ϔΞαϩϯɾϦϥΫϏϡʔςΟαϩϯͷ ݕࡧɾ༧໿αʔϏε w ϞόΠϧΞϓϦ J04"OESPJE 
 8FC 1$ɾεϚʔτϑΥϯ 
 ʹͯల։ w ϞόΠϧΞϓϦ޲͚"1*ͱ
 "OESPJEΞϓϦʹ"SDI6OJUͷಋೖ ϗοτϖούʔϏϡʔςΟʔͱ͸ 27 IUUQTCFBVUZIPUQFQQFSKQEPDHVJEFTBJTIJOEBUBIUNM

Slide 28

Slide 28 text

ϞόΠϧΞϓϦ޲͚APIͷΞʔΩςΫνϟʔ #BDLFOE
 "1*
 GPS403 #''"1*
 GPS40& J04"OESPJE ΞϓϦ %#ݕࡧΤϯδϯ 28 w #'' #BDLFOET'PS'SPOUFOET Λར༻ͨ͠
 ϚΠΫϩαʔϏεΞʔΩςΫνϟʔΛಋೖ w ΞϓϦέʔγϣϯن໛΍νʔϜߏ੒Λߟྀͨ͠40&ɾ403ͷ෼཭ʹΑͬͯ
 "1*։ൃεϐʔυͷߴ଎ԽΛ໨ࢦ͢

Slide 29

Slide 29 text

#BDLFOE"1* #''"1* දࣔཁ݅ʹؔΘΔॲཧ 40& 
 ΞϓϦಛ༗ͷॲཧ ΞϓϦͷϢʔεέʔεʹ߹Θͤͨ"1* 4QSJOH8FC'MVY,PUMJO 29 ٕज़ελοΫ΍੹຿ͷҧ͍ 4QSJOH.7$+BWB σʔλͷӬଓԽʹؔΘΔॲཧ 403 ۀ຿ϩδοΫ Ϧιʔεࢦ޲ͷ"1*

Slide 30

Slide 30 text

ϞόΠϧΞϓϦ޲͚APIͷΞʔΩςΫνϟʔ #BDLFOE
 "1*
 GPS403 #''"1*
 GPS40& J04"OESPJE ΞϓϦ %#ݕࡧΤϯδϯ 30 w ΞϓϦͷϢʔεέʔε୯ҐͰ࡞੒ͨ͠#''ͷ"1*͸
 σʔλͷϦιʔε୯ҐͰ࡞੒ͨ͠#BDLFOEͷ"1*͔Β
 ඞཁͳ৘ใΛू໿ͯ͠ΞϓϦʹఏڙ A C A B C B

Slide 31

Slide 31 text

#BDLFOE"1* #''"1* 31 ιϑτ΢ΣΞΞʔΩςΫνϟʔ $POUSPMMFSΫϥε૚ 3FQPTJUPSZΫϥε૚ 6TF$BTFΫϥε૚ υϝΠϯϞσϧ૚ $POUSPMMFSΫϥε૚ 3FQPTJUPSZΫϥε૚ 6TF$BTFΫϥε૚ 4FSWJDFΫϥε૚ ໼ҹ͸ґଘؔ܎ͷ޲͖

Slide 32

Slide 32 text

"OESPJEͷը໘Ϋϥε਺໿ #''ͷ"1*਺໿ 32 ਺ࣈͰݟΔΞϓϦέʔγϣϯͷن໛ #BDLFOEͷ"1*਺໿ ࢀর͢Δ%#ͷςʔϒϧ਺໿ w ೥ۙ͘ଓ͍͍ͯΔαʔϏεͰ͋Γɺ"1*͚ͩͰ΋ൺֱతେن໛ͳαʔϏε w چ"1*͔Βݱߦͷ#''ΞʔΩςΫνϟʔ΁ஈ֊తʹϦϓϨΠε͠ͳ͕Β
 ฏߦͰΤϯϋϯε΋࣮ࢪ

Slide 33

Slide 33 text

33 (༨ஊ) αʔϏεΛ͞Βʹ෼ׂ͠ͳ͍ͷ͔ʁ w νʔϜߏ੒΍ٕज़ཁҼ͔Β8FCΞϓϦ޲͚ͷ#''΍
 #BDLFOE"1*ͷυϝΠϯ෼ׂ͸࣮ࢪ͍ͯ͠ͳ͍ঢ়ଶ w ඞཁੑ͕͋Ε͹ࠓޙ࣮ࢪ͢ΔՄೳੑ͸͋Δ #BDLFOE"1* #''GPS8FC 33 #BDLFOE"1* #BDLFOE"1* #''GPS"QQ

Slide 34

Slide 34 text

IUUQTXXXTMJEFTIBSFOFU3FDSVJU-JGFTUZMFBQJC⒎CBDLFOE 34 ͞ΒͳΔৄࡉΛ஌Γ͍ͨํ͸ w ࡢ೥ͷ++6($$$4QSJOHͰ
 ฐࣾΤϯδχΞࡧख͕
 #''#BDLFOE෼ׂʹؔ͢Δ
 ൃදΛ͠·ͨ͠ w #''#BDLFOE෼ׂͷํ਑΍
 ઃܭɾ࣮૷ͷৄࡉΛ஌Γ͍ͨํ͸
 ͥͻ͝ཡԼ͍͞

Slide 35

Slide 35 text

ΞʔΩςΫνϟʔςετͷར༻๏ "SDI6OJUΛಋೖͨ͠ϓϩμΫτͷഎܠ ΞϓϦέʔγϣϯղੳ΁ͷར༻๏ "SDI6OJUͷ࢓૊Έͱ"1* "SDI6OJUͷجຊతͳػೳ

Slide 36

Slide 36 text

CASE 1 : υϝΠϯؒͷґଘؔ܎ͷ੍ޚ CASE 2 : ෆ҆ఆͳίʔυͷґଘؔ܎ͷ੍ޚ 36 ΞʔΩςΫνϟʔςετͷར༻๏

Slide 37

Slide 37 text

CASE 1 : υϝΠϯؒͷґଘؔ܎ͷ੍ޚ 37

Slide 38

Slide 38 text

w جຊతͳΞʔΩςΫνϟʔཧղ͸
 ઃܭυΩϡϝϯτΛ௨ͯ͡
 νʔϜϝϯόʔʹਁಁ͍ͯ͠Δঢ়ଶ w 13্ͰϨΠϠʔؒͷґଘؔ܎ʹ
 ؔ͢Δࢦఠ͕ൃੜ͢Δ͜ͱ΋ͳ͘
 ϨΠϠʔʹؔ͢ΔΞʔΩςΫνϟʔ ςετ΋ಛʹඞཁͱ͍ͯ͠ͳ͍ঢ়ଶ എܠɿϨΠϠʔؒͷґଘؔ܎ 38 $POUSPMMFSΫϥε૚ 3FQPTJUPSZΫϥε૚ 6TF$BTFΫϥε૚ υϝΠϯϞσϧ૚

Slide 39

Slide 39 text

w ϔΞαϩϯͱϦϥΫϏϡʔςΟʔαϩϯͰ͸༧໿ͷ࢓༷͕ҟͳΔͨΊ
 ࣅͨߏ଄͕ͩύοέʔδͷҟͳΔυϝΠϯϞσϧ΍%UPΫϥε͕ଘࡏ͢Δ w EPNBJONPEFMIBJSNFTTBHF%FMJWFSFE$PVQPO
 EPNBJONPEFMLJSFJNFTTBHF%FMJWFSFE$PVQPO
 JOGSBTUSVDUVSFEUPIBJSDPVQPO$PVQPO%JTDPVOU5ZQF
 JOGSBTUSVDUVSFEUPLJSFJDPVQPO$PVQPO%JTDPVOU5ZQF എܠɿରশతͳߏ଄Λ࣋ͭυϝΠϯ 39

Slide 40

Slide 40 text

՝୊ɿҙਤ͠ͳ͍υϝΠϯͷґଘ͕ൃੜ w *%&ͷΠϯϙʔτิ׬Λ࢖ͬͯίʔσΟϯάΛߦͬͨࡍ
 ޡͬͯຊདྷґଘͯ͠͸͍͚ͳ͍ผυϝΠϯͷΫϥεʹґଘͨ͠ίʔυΛ࣮૷ w ͨ·ͨ·࢓༷ͷࠩҟΛߟྀͨ͠65ͷςετέʔε͕࿙Εͯ͠·͍ͬͯͨͨΊ
 65΍ϨϏϡʔͰ΋ϛεΛݕ஌Ͱ͖ͣϚʔδ͞Εͯ͠·ͬͨ 40

Slide 41

Slide 41 text

TRYɿಛఆ৚݅ԼͰͷΠϯϙʔτΛ๷ࢭ 41 @RunWith(ArchUnitRunner.class) @AnalyzeClasses(packages = BASE_PACKAGE) public class HairKireiDependencyTest { @ArchTest static final ArchRule 
 hairύοέʔδͷΫϥε͕kireiύοέʔδͷΫϥεʹґଘͯ͠ͳ͍͜ͱΛ֬ೝ = classes() .that() // ύοέʔδ໊ͷύεʹhairΛؚΉΫϥεΛநग़ .resideInAPackage("..hair..") // Ұ෦ͷྫ֎Λআ͍ͯɺύοέʔδ໊ͷύεʹkireiΛؚΉΫϥε΁ͷґଘΛېࢭ .should(ArchConditions.onlyDependOnClassesThat(NOT_DEPEND_KIREI_CLASS)); }

Slide 42

Slide 42 text

TRYɿಛఆ৚݅ԼͰͷΠϯϙʔτΛ๷ࢭ 42 private static final DescribedPredicate NOT_DEPEND_KIREI_CLASS = new DescribedPredicate<>("must not depend on classes in kirei package") { 
 @Override public boolean apply(JavaClass input) { if (!input.getPackageName().contains("kirei")) { return true; } // @KireiResourceUsedByHairΞϊςʔγϣϯ͕෇༩͞Ε͍ͯΔΫϥε͸ྫ֎తʹґଘΛڐՄ͢Δ if (input.isAnnotatedWith(KireiResourceUsedByHair.class)) { return true; } } };

Slide 43

Slide 43 text

TRYɿಛఆ৚݅ԼͰͷΠϯϙʔτΛ๷ࢭ 43 ϨΠϠʔ಺ͷґଘؔ܎Λ੍ޚ͢Δ IUUQTXXXPSFJMMZDPNMJCSBSZWJFX
 TPGUXBSFBSDIJUFDUVSFQBUUFSOT

Slide 44

Slide 44 text

ิ଍ɿϞδϡʔϧ෼ׂͱͷൺֱ 44 ϨΠϠʔ಺ͷґଘؔ܎Λ੍ޚ͢Δ IUUQTXXXPSFJMMZDPNMJCSBSZWJFX
 TPGUXBSFBSDIJUFDUVSFQBUUFSOT w Ϟδϡʔϧ෼ׂͰґଘؔ܎Λ
 ੍ޚͰ͖͍ͯΔͳΒͦΕͰे෼ w ྫ֎έʔε͕͋ͬͯ෼ׂͮ͠Β͍
 ·ͩ෼ׂ͢Δ΄Ͳͷن໛Ͱ΋ͳ͍
 কདྷͷ෼ׂʹઌཱͬͨୈҰา
 ͱ͍ͬͨ৔߹ʹ͸༗༻

Slide 45

Slide 45 text

45 CASE 2 : ෆ҆ఆͳίʔυͷґଘؔ܎ͷ੍ޚ

Slide 46

Slide 46 text

IUUQTUXJUUFSDPNKBIOOJFTUBUVT 46 w ༏ઌ౓΍֎෦؀ڥͳͲͷཁҼͰ
 ࣮૷͕Ұ෦׬ྃͭͭ͠΋
 ͳ͔ͳ͔ϦϦʔεͰ͖ͳ͍ϒϥϯν͕ଘࡏ w ϒϥϯνͷϥΠϑλΠϜ͕ෆ༻ҙʹ௕͘ ͳͬͯ͠·͓ͬͯΓίϯϑϦΫτ͕
 ൃੜ͠΍͘͢ͳ͍ͬͯͨ എܠɿฏߦ͢Δϒϥϯνͱίʔυͷෳࡶ͞

Slide 47

Slide 47 text

എܠɿfeatureτάϧʹΑΔϒϥϯνͷ࡟ݮ 47 IUUQTXXXNBSUJOGPXMFSDPNBSUJDMFTGFBUVSFUPHHMFTIUNM w ฏߦ͢Δϒϥϯνͷ਺ΛݮΒͨ͢Ίʹ
 ੩తͳϦϦʔετάϧΛಋೖͯ͠
 ϝΠϯϒϥϯνʹϚʔδ w ࣮ͨͩݧతʹϦϦʔε͢Δ಺༰Ͱ͋Γ
 ࢓༷͕ॾࣄ৘Ͱෆ҆ఆͳঢ়ଶͩͬͨ w ͕ͨͬͯ͠ଞͷ҆ఆͨ͠ίʔυ͔Β
 ෆ҆ఆͳίʔυʹґଘͤͨ͘͞ͳ͍

Slide 48

Slide 48 text

՝୊ɿෆ҆ఆͳίʔυ͕ϝΠϯϥΠϯʹ߹ྲྀ 48 #PPLNBSL6TFDBTF #PPLNBSL3FQPTJUPSZ υϝΠϯϞσϧ $PVQPO6TFDBTF #PPLNBSL4FSWJDF $PVQPO4FSWJDF $PVQPO3FQPTJUPSZ υϝΠϯϞσϧ ໼ҹ͸ґଘؔ܎ͷ޲͖ &YQFSJNFOUBM6TFDBTF &YQFSJNFOUBM4FSWJDF &YQFSJNFOUBM3FQPTJUPSZ υϝΠϯϞσϧ

Slide 49

Slide 49 text

՝୊ɿෆ҆ఆͳίʔυ͕ϝΠϯϥΠϯʹ߹ྲྀ 49 #PPLNBSL6TFDBTF #PPLNBSL3FQPTJUPSZ υϝΠϯϞσϧ $PVQPO6TFDBTF &YQFSJNFOUBM6TFDBTF #PPLNBSL4FSWJDF $PVQPO4FSWJDF &YQFSJNFOUBM4FSWJDF $PVQPO3FQPTJUPSZ &YQFSJNFOUBM3FQPTJUPSZ υϝΠϯϞσϧ ໼ҹ͸ґଘؔ܎ͷ޲͖ υϝΠϯϞσϧ

Slide 50

Slide 50 text

՝୊ɿෆ҆ఆͳίʔυ͕ϝΠϯϥΠϯʹ߹ྲྀ 50 #PPLNBSL6TFDBTF #PPLNBSL3FQPTJUPSZ υϝΠϯϞσϧ $PVQPO6TFDBTF &YQFSJNFOUBM6TFDBTF #PPLNBSL4FSWJDF $PVQPO4FSWJDF &YQFSJNFOUBM4FSWJDF $PVQPO3FQPTJUPSZ &YQFSJNFOUBM3FQPTJUPSZ υϝΠϯϞσϧ ໼ҹ͸ґଘؔ܎ͷ޲͖ υϝΠϯϞσϧ

Slide 51

Slide 51 text

՝୊ɿෆ҆ఆͳίʔυ͕ϝΠϯϥΠϯʹ߹ྲྀ 51 #PPLNBSL6TFDBTF #PPLNBSL3FQPTJUPSZ υϝΠϯϞσϧ $PVQPO6TFDBTF &YQFSJNFOUBM6TFDBTF #PPLNBSL4FSWJDF $PVQPO4FSWJDF &YQFSJNFOUBM4FSWJDF $PVQPO3FQPTJUPSZ &YQFSJNFOUBM3FQPTJUPSZ υϝΠϯϞσϧ ໼ҹ͸ґଘؔ܎ͷ޲͖ υϝΠϯϞσϧ

Slide 52

Slide 52 text

52 #PPLNBSL6TFDBTF #PPLNBSL3FQPTJUPSZ υϝΠϯϞσϧ $PVQPO6TFDBTF &YQFSJNFOUBM6TFDBTF #PPLNBSL4FSWJDF $PVQPO4FSWJDF &YQFSJNFOUBM4FSWJDF $PVQPO3FQPTJUPSZ &YQFSJNFOUBM3FQPTJUPSZ υϝΠϯϞσϧ ໼ҹ͸ґଘؔ܎ͷ޲͖ υϝΠϯϞσϧ TRYɿෆ҆ఆͳίʔυ΁ґଘͰ͖ΔΫϥε΍
 ɹɹ ϝιουΛςετʹΑΓ੍ݶ͢Δ

Slide 53

Slide 53 text

TRYɿෆ҆ఆͳίʔυ΁ґଘͰ͖ΔΫϥε΍
 ɹɹ ϝιουΛςετʹΑΓ੍ݶ͢Δ 53 @ArchTest val `ExperimentalΞϊςʔγϣϯ͕෇༩͞ΕͨΫϥε͕
 ExperimentalΞϊςʔγϣϯ͕෇༩͞ΕͨΫϥε͔ΒͷΈґଘ͞Ε͍ͯΔ͜ͱΛ֬ೝ` = 
 classes() .that() .areAnnotatedWith(Experimental::class.java) .should(ArchConditions.onlyHaveDependentClassesThat(ANNOTATED_CLASSES)) @ArchTest val `ExperimentalΞϊςʔγϣϯ͕෇༩͞Εͨϝιου͕
 ExperimentalΞϊςʔγϣϯ͕෇༩͞Εͨϝιου΍Ϋϥε͔ΒͷΈґଘ͞Ε͍ͯΔ͜ͱΛ֬ೝ` = 
 methods() .that() .areAnnotatedWith(TEExperimental::class.java) .should(DEPENDENT_ONLY_ON_ANNOTATED_CODE)

Slide 54

Slide 54 text

TRYɿෆ҆ఆͳίʔυ΁ґଘͰ͖ΔΫϥε΍
 ɹɹ ϝιουΛςετʹΑΓ੍ݶ͢Δ 54 private fun checkRecursively(input: JavaClass): Boolean { return when { // ExperimentalΞϊςʔγϣϯ͸਌Ϋϥεʹ͔͠෇༩͞Ε͍ͯͳ͍ͷͰ // ಗ໊Ϋϥε΍ωετͷࢠΫϥεͷ৔߹͸࠶ؼతʹ਌ΫϥεΛݟʹߦ͘ input.isAnonymousClass || input.isNestedClass -> checkRecursively( input.enclosingClass.get() ) input.isAnnotatedWith(Experimental::class.java) -> true else -> false } }

Slide 55

Slide 55 text

ΞʔΩςΫνϟʔςετͷར༻๏ "SDI6OJUΛಋೖͨ͠ϓϩμΫτͷഎܠ ΞϓϦέʔγϣϯղੳ΁ͷར༻๏ "SDI6OJUͷ࢓૊Έͱ"1* "SDI6OJUͷجຊతͳػೳ

Slide 56

Slide 56 text

56 ΞϓϦέʔγϣϯղੳ΁ͷར༻๏ CASE 3 : PRࠩ෼ͷӨڹൣғͷݕ஌ CASE 4 : ࢓༷೺ѲϚοϓͷࣗಈ࡞੒ CASE 5 : αʔϏεؒґଘؔ܎ͷՄࢹԽ CASE 6 : ը໘ભҠਤͷࣗಈੜ੒

Slide 57

Slide 57 text

CASE 3 : PRࠩ෼ͷӨڹൣғͷݕ஌ 57

Slide 58

Slide 58 text

w ଟ͘ͷΫϥε͔Βґଘ͞Ε͍ͯΔඃґଘ౓͕
 ߴ͍ΫϥεͷΤϯϋϯε΍ϦϑΝΫλͰ͸
 ൺֱతมߋͷӨڹൣғ͕େ͖͘
 σάϨ͕ൃੜ͢Δ֬཰͕ߴ͍ w ґଘΫϥεଆͰ͸ίʔυ্ͷࠩ෼͕Ͱ΋
 ಺෦࢓༷΁ͷ҉໧తͳґଘʹΑͬͯ
 ࢓༷ͷഁյతมߋͷӨڹΛड͚Δ৔߹͕͋Δ 58 എܠɿࠩ෼ͷӨڹൣғͱσάϨ // Τϯϋϯεલ : ਖ਼ͷ੔਺Λฦ͢
 // Τϯϋϯεޙ : ੔਺Λฦ͢
 public int calculate(int input){ var output = ... return output; } 
 // calculate()͕ਖ਼ͷ੔਺Λฦ͢ͱ͍͏
 // લఏʹґଘͯ͠ॲཧΛ࣮ߦ͢Δϝιου public int useCalculate(){ var result = calculate(10); return ... }

Slide 59

Slide 59 text

w 13ͷࠩ෼ͷӨڹൣғ
 σάϨ͕ൃੜ͢Δ
 Մೳੑ͕͋ΔΫϥε΍$POUSPMMFSΫϥε 
 Λػցతʹ೺Ѳ͍ͨ͠ w *%&Λ࢖ͬͯಛఆΫϥεʹର͢Δ
 ґଘؔ܎ͷϦετΞοϓ΋Մೳ͕ͩ
 13ͷࠩ෼ͷશΫϥεʹର࣮ͯ͠ߦͯ͠
 ϝϯόʔʹڞ༗͢Δͷʹ͸ख͕͔͔ؒΔ 59 ՝୊ɿࠩ෼ͷӨڹൣғͷࣗಈݕग़

Slide 60

Slide 60 text

w 13ͷࠩ෼ͷӨڹൣғ
 σάϨογϣϯ͕ൃੜ͢ΔՄೳੑ͕
 ɹ͋ΔΫϥε΍$POUSPMMFSΫϥε 
 ͷҰཡදΛ$*্Ͱࣗಈੜ੒ͯ͠
 13্Ͱ೺ѲͰ͖ΔΑ͏ʹͨ͠ w ϝϯόʔಉ࢜ͰӨڹൣғͷೝࣝΛ
 ڞ༗͢Δ͜ͱͰςετͰ๷͍͛ͯͳ͍
 σάϨογϣϯʹؾ͖ͮ΍͘͢͢Δ 60 TRYɿCI্͔ΒӨڹൣғΛࣗಈͰϦετΞοϓ

Slide 61

Slide 61 text

w "13ͰEJ⒎͕ൃੜͨ͠Ϋϥε
 #"ʹґଘ͍ͯ͠ΔΫϥε
 $#ʹґଘ͍ͯ͠ΔΫϥεɾɾɾ
 ͷΑ͏ʹґଘΫϥεΛॱʹ୧͍͚ͬͯ͹
 EJ⒎ͷӨڹൣғΛચ͍ग़͢͜ͱ͕Մೳ w EJ⒎͕ൃੜͨ͠ϑΝΠϧ໊Λ༩͑Δͱ
 ӨڹൣғͷϑΝΠϧ໊Λग़ྗ͢ΔΑ͏ͳ
 HSBEMFλεΫΛ"SDI6OJUΛ࢖࣮ͬͯ૷ 61 HowɿPRͷdiff͔ΒӨڹൣғΛׂΓग़͢ ›./gradlew scanDependenciesFromGitDiff

Slide 62

Slide 62 text

ิ଍ɿݕग़Ͱ͖ͳ͍Өڹൣғʹ஫ҙ w ੩తͳґଘղܾͰ͸ݕग़Ͱ͖ͳ͍έʔεʹ஫ҙ͢Δ
 ϦϑϨΫγϣϯͳͲಈతͳґଘղܾ
 ΞϓϦέʔγϣϯϑϨʔϜϫʔΫʹؔΘΔมߋ
 ઃఆϑΝΠϧͷมߋ w ͋͘·ͰӨڹൣғͷ೺Ѳͷαϙʔτͱͯ͠࢖͏ w σάϨογϣϯ๷ࢭ͕໨తͳΒ͹
 ΑΓྑ͍ઃܭ΍ςετͰͦ΋ͦ΋σάϨογϣϯ͕
 ൃੜ͠ʹ͘͘͢Δͷʹӽͨ͜͠ͱ͸ͳ͍ 62 03FJMMZ.FEJB *OD
 ʮ'VOEBNFOUBMTPG4PGUXBSF"SDIJUFDUVSFʯ

Slide 63

Slide 63 text

63 TRYɿӨڹൣғͷςετ͚ͩΛ࣮ߦ͢Δ w 13ͷEJ⒎ͷӨڹൣғ͕ར༻ͯ͠
 Өڹൣғ಺ͷςετ͚ͩΛ
 ࣮ߦͤ͞Δ͜ͱ΋Մೳ w ࣮ߦίετ͕ߴ͍ςετΛ
 ޮ཰Α࣮͘ߦ͢Δ͜ͱ΋Ͱ͖Δ w ςετϐϥϛουΛ
 ॎʹ෼ׂ͢ΔΠϝʔδ 4NBMM .FEJVN -BSHF ςετϐϥϛουΛॎʹ෼ׂ͢Δ

Slide 64

Slide 64 text

ิ଍ɿӨڹൣғʹԠͨࣗ͡ಈςετൣғͷมߋ w DG5FTU*NQBDU"OBMZTJT
 ΫϥεؒͷӨڹ౓߹͍Λࣄલʹอଘ͓͖ͯ͠
 มߋ͞ΕͨΫϥεͱؔ࿈౓͕ߴ͍
 Ϋϥεͷςετ͚ͩΛ࣮ߦ
 7JTVBM4UVEJP΍"[VSF1JQFMJOFT্Ͱར༻Մೳ 64 IUUQTNBSUJOGPXMFSDPNBSUJDMFTSJTFUFTUJNQBDUBOBMZTJTIUNM

Slide 65

Slide 65 text

CASE 4 : ࢓༷೺ѲϚοϓͷࣗಈ࡞੒ 65

Slide 66

Slide 66 text

എܠɿྺ࢙͕௕͍αʔϏεͳΒͰ͸ͷن໛(࠶) 66 "OESPJEͷը໘਺໿ #''ͷ"1*਺໿ #BDLFOEͷ"1*਺໿ ࢀর͢Δ%#ͷςʔϒϧ਺໿ w ೥ۙ͘ଓ͍͍ͯΔαʔϏεͰ͋Γɺ"1*͚ͩͰ΋ൺֱతେن໛ͳαʔϏε

Slide 67

Slide 67 text

՝୊ɿϝϯόʔͷ࢓༷ཧղ౓͕ภͬͯ͠·͏ w ཭೚ɾҟಈͳͲͷӨڹ΋͋Γɺෳࡶ͔ͭॏཁͳ࢓༷ͷ"1*Λ
 ࣮૷ϨϕϧͰৄࡉʹཧղ͍ͯ͠Δϝϯόʔ͕ภͬͯ͠·͏͜ͱ͕͋Δ w ୭͕Ͳͷ"1*ͷ࢓༷ʹৄͦ͠͏ͳͷ͔
 ͱ͍͏͜ͱࣗମΛ೺Ѳ͍ͯ͠Δϝϯόʔ΋ݶΒΕ͖ͯͯ͠·͏ w ϝϯόʔͷ࢓༷ཧղ౓͕ՄࢹԽͰ͖Ε͹
 λεΫ΍ϨϏϡΞʔͷΞαΠϯͷ൑அʹ΋׆༻Ͱ͖Δ 67

Slide 68

Slide 68 text

՝୊ɿ࢓༷ͷҰཡԽ΍ӡ༻΋ίετ͕ߴ͍ IUUQTFOHJOFFSSFDSVJUMJGFTUZMFDPKQUFDICMPHTQFDVOEFSTUBOEJOHNBQ 68

Slide 69

Slide 69 text

TRYɿίϛοτཤྺͱґଘؔ܎͔Βࣗಈੜ੒ w ίϛοτͰEJ⒎͕͋ΔΫϥεʹ
 ґଘ͍ͯ͠ΔΫϥεΛ୧Ε͹
 Ͳ͔͜ͷ"1*ΤϯυϙΠϯτʹඥͮ͘ w ֤ϝϯόʔ͕ͦΕͧΕͷ"1*ʹ
 ͲΕ͚ͩมߋΛՃ͑ͨ͜ͱ͕
 ͋Δ͔Λ਺஋ԽͰ͖Δ 69 ›./gradlew calculateContributeScore

Slide 70

Slide 70 text

Howɿίϛοτཤྺͱґଘؔ܎͔Βࣗಈੜ੒ w (JU)VCͷ"1*Λར༻ͯ͠
 ֤νʔϜϝϯόʔ͕ࠓ·Ͱͷ13্Ͱ
 ͲͷΫϥεʹͲͷ͘Β͍ࠩ෼Λ
 ࡞͖ͬͯͨͷ͔Λूܭ w "SDI6OJUΛ࢖ͬͯ
 ֤Ϋϥε͕Ͳͷ"1*ΤϯυϙΠϯτͰ
 ར༻͞Ε͍ͯΔ͔ͷґଘؔ܎Λऔಘ 70 ›./gradlew calculateContributeScore

Slide 71

Slide 71 text

TRYɿίϛοτཤྺͱґଘؔ܎͔Βࣗಈੜ੒ 71

Slide 72

Slide 72 text

ิ଍ɿ࢓༷ཧղ౓͕ͲΕ͘Β͍ਖ਼͍͠ͷ͔ʁ w ίϛοτܦݧ͕͋Δͱ͍ͬͯ΋ܰඍͳϦϑΝΫλͩͬͨΓͱ
 ͦΕ΄Ͳ࢓༷ཧղ౓ͱϦϯΫͯ͠ͳ͍έʔε΋͋Δ w ίϛοτ΍13ͷEJ⒎ͷେ͖͞΍ಉ࣌ฤूͨ͠ϑΝΠϧ਺ͳͲ΋
 ߟྀͯ͠ॏΈ෇͚Λ͢Δͱਫ਼౓্͕͕Δ͔΋͠Εͳ͍ w ࠓճͷϢʔεέʔεͰ͸͋͘·Ͱେ·͔ͳ܏޲͑͞
 Θ͔Ε͹Α͍ͱׂΓ੾͍ͬͯΔ w DGϖΞϓϩάϥϛϯάɾϞϒϓϩάϥϛϯάɾ҉໧஌ͷڞ༗ 72

Slide 73

Slide 73 text

CASE 5 : αʔϏεؒґଘؔ܎ͷՄࢹԽ 73

Slide 74

Slide 74 text

74 എܠɿAPIͷΞʔΩςΫνϟʔ (࠶) w ΞϓϦͷϢʔεέʔε୯ҐͰ࡞੒ͨ͠#''ͷ"1*͸
 σʔλͷϦιʔε୯ҐͰ࡞੒ͨ͠#BDLFOEͷ"1*͔Β
 ඞཁͳ৘ใΛू໿ͯ͠ΞϓϦʹఏڙ #BDLFOE
 "1*
 GPS403 #''"1*
 GPS40& J04"OESPJE ΞϓϦ %#ݕࡧΤϯδϯ A C A B C B

Slide 75

Slide 75 text

എܠɿྺ࢙͕௕͍αʔϏεͳΒͰ͸ͷن໛(࠶) 75 "OESPJEͷը໘਺໿ #''ͷ"1*਺໿ #BDLFOEͷ"1*਺໿ ࢀর͢Δ%#ͷςʔϒϧ਺໿ w ೥ۙ͘ଓ͍͍ͯΔαʔϏεͰ͋Γɺ"1*͚ͩͰ΋ൺֱతେن໛ͳαʔϏε

Slide 76

Slide 76 text

՝୊ɿαʔϏεؒͷӨڹௐࠪͷίετ͕ߴ͍ w ΤϯϋϯεɾϦϑΝΫλ࣌ʹ͓͚ΔαʔϏεؒͷӨڹௐࠪ
 Ͳͷ#BDLFOEͷ"1*͕Ͳͷ#''ͷ"1*Ͱ࢖ΘΕ͍ͯΔͷ͔
 Ͳͷ#''ͷ"1*͕ͲͷΞϓϦͷը໘Ͱ࢖ΘΕ͍ͯΔͷ͔ w ֤"1*͕ͦΕͧΕϢʔβʔʹͲͷΑ͏ͳମݧΛ
 ఏڙ͢ΔͨΊʹར༻͞Ε͍ͯΔͷ͔ w ͜ΕΒͷυΩϡϝϯτΛखಈͰϝϯςφϯε͢Δͷ΋େม 76

Slide 77

Slide 77 text

TRYɿAPIؒͷґଘؔ܎දΛCI্Ͱࣗಈੜ੒ 77 #BDLFOE
 "1*
 GPS403 #''"1*
 GPS40& J04"OESPJE ΞϓϦ %#ݕࡧΤϯδϯ

Slide 78

Slide 78 text

w "SDI6OJUΛ$PSF"1*Λ࢖ͬͯ
 ϝιου͕ݺͼग़͢ϝιουΛ
 औಘ͢Δ͜ͱ͕Ͱ͖Δ w ͜ΕΛར༻ͯ͠
 ͋Δ"1*ΤϯυϙΠϯτ͕
 σʔλΛऔಘ͢Δ·Ͱͷ
 ϝιουγʔέϯεΛ
 ίʔυ͔Β௥͏͜ͱ͕Ͱ͖Δ 78 HowɿΤϯυϙΠϯτͱ֎෦σʔλͷରԠΛ
 ϝιουͷґଘؔ܎͔Βऔಘ

Slide 79

Slide 79 text

w ύοέʔδ΍Ϋϥεͷ
 ϨΠϠʔຖͷ໋໊نଇΛ
 ߟྀ͢Ε͹ΑΓޮ཰తʹ
 γʔέϯεΛ࡞੒Մೳ w ϥϜμؔ਺΍,PUMJO+7.͕
 ͲΜͳόΠτίʔυʹͳΔ͔͸
 एׯߟྀ͢Δඞཁ͕͋Δ৔߹΋
 ͋Δ͜ͱʹ஫ҙ 79 HowɿΤϯυϙΠϯτͱ֎෦σʔλͷରԠΛ
 ϝιουͷґଘؔ܎͔Βऔಘ $POUSPMMFSΫϥε૚ 3FQPTJUPSZΫϥε૚ 6TF$BTFΫϥε૚ υϝΠϯϞσϧ૚

Slide 80

Slide 80 text

80 @RestController @RequestMapping(value = "/hair/hair-colors") @AllArgsConstructor public class HairColorController { private final HairColorUsecase usecase; @GetMapping public ResponseEntity getHairColor() { final var body = usecase.getHairColors(); return ResponseEntity.body(body); } } w "1*ΤϯυϙΠϯτ
 IBJSIBJSDPMPST(&5͸ )BJS$PMPS6TFDBTFΫϥεͷ
 HFU)BJS$PMPSTϝιουʹ
 ґଘ͍ͯ͠Δ w Ξϊςʔγϣϯͷ஋͔Β
 "1*ΤϯυϙΠϯτͷύε΍
 ϝιου΋औಘͰ͖Δ HowɿΤϯυϙΠϯτͱ֎෦σʔλͷରԠΛ
 ϝιουͷґଘؔ܎͔Βऔಘ

Slide 81

Slide 81 text

81 @RestController @RequestMapping(value = "/hair/hair-colors") @AllArgsConstructor public class HairColorController { private final HairColorUsecase usecase; @GetMapping public ResponseEntity getHairColor() { final var body = usecase.getHairColors(); return ResponseEntity.body(body); } } w "1*ΤϯυϙΠϯτ
 IBJSIBJSDPMPST(&5͸ )BJS$PMPS6TFDBTFΫϥεͷ
 HFU)BJS$PMPSTϝιουʹ
 ґଘ͍ͯ͠Δ HowɿΤϯυϙΠϯτͱ֎෦σʔλͷରԠΛ
 ϝιουͷґଘؔ܎͔Βऔಘ

Slide 82

Slide 82 text

82 @Usecase @AllArgsConstructor public class HairColorUsecase { private final HairColorRepository repository; @Nonnull public GetHairColorResponse getHairColors() { final var hairColors = repository.findAll(); final var hairColorDtos = hairColors.stream() .map(HairColorDtoMapper::map) .collect(Collectors.toList()); return GetHairColorResponse.builder() .hairColors(hairColorDtos) .build(); } } w )BJS$PMPS6TFDBTFΫϥεͷ
 HFU)BJS$PMPSTϝιου͸
 )BJS$PMPS3FQPTJUPSZΫϥεͷ
 pOE"MMϝιουʹґଘ͍ͯ͠Δ HowɿΤϯυϙΠϯτͱ֎෦σʔλͷରԠΛ
 ϝιουͷґଘؔ܎͔Βऔಘ

Slide 83

Slide 83 text

83 @Usecase @AllArgsConstructor public class HairColorUsecase { private final HairColorRepository repository; @Nonnull public GetHairColorResponse getHairColors() { final var hairColors = repository.findAll(); final var hairColorDtos = hairColors.stream() .map(HairColorDtoMapper::map) .collect(Collectors.toList()); return GetHairColorResponse.builder() .hairColors(hairColorDtos) .build(); } } w )BJS$PMPS6TFDBTFΫϥεͷ
 HFU)BJS$PMPSTϝιου͸
 )BJS$PMPS3FQPTJUPSZΫϥεͷ
 pOE"MMϝιουʹґଘ͍ͯ͠Δ HowɿΤϯυϙΠϯτͱ֎෦σʔλͷରԠΛ
 ϝιουͷґଘؔ܎͔Βऔಘ

Slide 84

Slide 84 text

TRYɿAPIؒͷґଘؔ܎දΛCI্Ͱࣗಈੜ੒ 84 #BDLFOE
 "1*
 GPS403 #''"1*
 GPS40& J04"OESPJE ΞϓϦ %#ݕࡧΤϯδϯ

Slide 85

Slide 85 text

TRYɿAPI/DBςʔϒϧؒͷґଘؔ܎ΛҰཡԽ 85 #BDLFOE
 "1*
 GPS403 #''"1*
 GPS40& J04"OESPJE ΞϓϦ %#ݕࡧΤϯδϯ

Slide 86

Slide 86 text

TRYɿAndroidɾBFFؒͷґଘؔ܎ΛҰཡԽ 86 #BDLFOE
 "1*
 GPS403 #''"1*
 GPS40& J04"OESPJE ΞϓϦ %#ݕࡧΤϯδϯ

Slide 87

Slide 87 text

TRYɿAndroidɾDBؒͷґଘؔ܎ΛҰཡԽ 87 #BDLFOE
 "1*
 GPS403 #''"1*
 GPS40& J04"OESPJE ΞϓϦ %#ݕࡧΤϯδϯ

Slide 88

Slide 88 text

IUUQTRJJUBDPNPYTPGUJUFNTEDDDDDFD 88

Slide 89

Slide 89 text

89 ิ଍ɿAPIͷΰʔϧΩϟϯόε w "1*ͱΞϓϦͷը໘ͷରԠ͕ҰཡԽ͞ΕΔ͜ͱͰνʔϜؒͰ΋
 "1*ͷ඼࣭ͱϢʔβʔӨڹͷ໨ઢ͕߹Θͤ΍͘͢ͳΔ
 ͋Δ"1*ͷϨεϙϯε͕஗͘ͳΔͱϢʔβʔͷͲͷମݧʹӨڹ͕ͰΔͷ͔
 ͋Δ"1*ͰΤϥʔ͕ଟ͘ͳΔͱϢʔβʔ͸Կ͕Ͱ͖ͳ͘ͳͬͯ͠·͏ͷ͔ ᠳӭࣾʮ8FC"1*ͷઃܭʯ

Slide 90

Slide 90 text

ิ଍ɿ෼ࢄτϨʔγϯά w αʔϏεؒͷґଘؔ܎Λ೺Ѳ͍͚ͨͩ͠ͳΒ͹
 ෼ࢄτϨʔγϯάͳͲΛར༻͢Δ͜ͱ΋༗ޮ 90

Slide 91

Slide 91 text

CASE 6: ը໘ભҠਤͷࣗಈੜ੒ 91

Slide 92

Slide 92 text

92 എܠɿAPIͷΰʔϧΩϟϯόε w "1*ͱϢʔβʔମݧ΁ͷӨڹΛߟ͑Δʹ͋ͨͬͯ
 ෳ਺ͷ"1*͕ͲͷΑ͏ͳॱ൪Ͱར༻͞Ε͍ͯΔͷ͔Λ೺Ѳ͍ͨ͠
 ΞϓϦ։ൃऀ͸ͲͷΑ͏ͳॱ൪Λ"1*Λར༻͍ͯ͠Δͷ͔
 Ϣʔβʔ͸ͲͷΑ͏ʹը໘ΛભҠ͍ͯ͠Δͷ͔ w ϩά͔Βύλʔϯ΍܏޲Λ෼ੳ͢Δ͚ͩͰͳ͘
 ͦ΋ͦ΋ը໘࢓༷ͱͯ͠Ͳ͏͍͏ભҠ͕Մೳͳͷ͔΋஌Γ͍ͨ

Slide 93

Slide 93 text

93 ՝୊ɿAPIͷΰʔϧΩϟϯόε w ը໘਺͕ଟ͍େن໛ΞϓϦͩͱීஈ͔ΒΞϓϦΛ৮͍ͬͯͯ΋
 ΄ͱΜͲ։͔ͳ͍ը໘͕͋ΔͳͲશͯͷը໘ભҠΛ೺Ѳ͢Δͷ͸೉͍͠ w ΞϓϦͷը໘ભҠύλʔϯ࢓༷ΛखಈͰϝϯςϯφϯε͢Δͷ͸େม

Slide 94

Slide 94 text

94 TRYɿը໘ભҠਤͷࣗಈੜ੒

Slide 95

Slide 95 text

95 Howɿը໘Ϋϥεͷґଘؔ܎Λऔಘ w "OESPJEΞϓϦͷ
 "DUJDJUZɾ'SBHNFOUΫϥεͷ
 ݺͼग़͠ͳͲͷґଘؔ܎Λར༻ͯ͠
 Ϣʔβʔ͕Ͳͷը໘͔ΒͲͷը໘ʹ
 ભҠ͠ಘΔͷ͔ͷભҠάϥϑΛ࡞੒ w ը໘ભҠάϥϑΛը૾ͱͯ͠ग़ྗ private fun onClickBlog() { val intent = 
 KireiBlogListTabActivity.intent(
 this, 
 salonId
 ) startActivity(intent) }
 
 private fun navigateToLogin() { val intent = LoginActivity.intent(this) startActivityForResult( intent, REQUEST_CODE_LOGIN ) }

Slide 96

Slide 96 text

ิ଍ɿAndroidΞϓϦͱArchUnit w ΞϓϦέʔγϣϯͷߏ੒ʹΑͬͯ͸+BWBɾ,PUMJOίʔυ͚ͩͰͳ͘
 9.-ϑΝΠϧͷ಺༰΋֬ೝ͢Δඞཁ͕͋Δ͜ͱʹ஫ҙ
 DG/BWJHBUJPO$PNQPOFOUͱφϏήʔγϣϯάϥϑ w "SDI6OJU͸CZUFDPEFΛղੳ͢ΔͷͰࣗಈੜ੒͞Εͨ
 Ϋϥεͷґଘؔ܎΋ߟྀ͢Δ͜ͱ͕Ͱ͖Δ 96

Slide 97

Slide 97 text

ΞʔΩςΫνϟʔςετͷར༻๏ "SDI6OJUΛಋೖͨ͠ϓϩμΫτͷഎܠ ΞϓϦέʔγϣϯղੳ΁ͷར༻๏ "SDI6OJUͷ࢓૊Έͱ"1* "SDI6OJUͷجຊతͳػೳ

Slide 98

Slide 98 text

CASE 1 : υϝΠϯؒͷґଘؔ܎ͷ੍ޚ CASE 2 : ෆ҆ఆͳίʔυͷґଘؔ܎ͷ੍ޚ 98 ΞʔΩςΫνϟʔςετͷར༻๏

Slide 99

Slide 99 text

99 ΞϓϦέʔγϣϯղੳ΁ͷར༻๏ CASE 3 : PRࠩ෼ͷӨڹൣғͷݕ஌ CASE 4 : ࢓༷೺ѲϚοϓͷࣗಈ࡞੒ CASE 5 : αʔϏεؒґଘؔ܎ͷՄࢹԽ CASE 6 : ը໘ભҠਤͷࣗಈੜ੒

Slide 100

Slide 100 text

w "SDI6OJUͱ࢖͏ͱ+BWB΍,PUMJO+7.Ͱهड़͞Εͨ
 ΞϓϦέʔγϣϯͷΞʔΩςΫνϟʔςετ΍
 ґଘؔ܎ͷղੳͳͲ͕ൺֱతָʹ࣮૷Ͱ͖Δ w ϥΠϒϥϦͷݩʑͷ໨తͱ͸ҟͳΔ࢖͍ํͰ͋Δ͜ͱʹ஫ҙ w खಈͰͷӡ༻͸໘౗͚ͩͲؤுͬͯࣗಈԽ͢Δʹ͸ՙ͕ॏ͍
 ͦΜͳᙱ͍ͱ͜ΖʹखΛಧ͔ͤΔΑ͏ͳࣗಈԽΛ
 "SDI6OJUΛ࢖࣮ͬͯݱ͢Δ͜ͱ͕Ͱ͖Δ ·ͱΊ 100

Slide 101

Slide 101 text

w IUUQTXXXBSDIVOJUPSH w IUUQTNFEJVNDPNYFCJBGSBODFFOGPSDJOHBSDIJUFDUVSFEFDJTJPOT XJUIBSDIVOJUECGDGB w IUUQTCMPHTPSBDMFDPNPUOKQVOJUUFTUZPVSBSDIJUFDUVSFXJUI BSDIVOJUKB w IUUQTBTNPXJP w IUUQTHJUIVCDPN5/("SDI6OJU&YBNQMFT ࢀߟURL : 1 101

Slide 102

Slide 102 text

w IUUQTXXXPSFJMMZDPNMJCSBSZWJFX
 TPGUXBSFBSDIJUFDUVSFQBUUFSOT w IUUQTNBSUJOGPXMFSDPNBSUJDMFTSJTFUFTUJNQBDU BOBMZTJTIUNMGPPUOPUFKBWBUJB w IUUQTEPDTNJDSPTPGUDPNFOVTB[VSFEFWPQTQJQFMJOFTUFTU UFTUJNQBDUBOBMZTJT WJFXB[VSFEFWPQT ࢀߟURLɿ2 102

Slide 103

Slide 103 text

ΞʔΩςΫνϟʔςετͷར༻๏ "SDI6OJUΛಋೖͨ͠ϓϩμΫτͷഎܠ ΞϓϦέʔγϣϯղੳ΁ͷར༻๏ "SDI6OJUͷ࢓૊Έͱ"1* "SDI6OJUͷجຊతͳػೳ