Slide 1

Slide 1 text

)PX.JDSPTFSWJDFTBSF MJOLFEBU$PPLQBE  ΫοΫύουגࣜձࣾ ٕज़෦։ൃج൫( ٢઒ਸྙ

Slide 2

Slide 2 text

ࠓճ࿩͞ͳ͍͜ͱ w.JDSPTFSJWDFTͱ͸ʙ w.BSUJO'PXMFS͕ʙ wಋೖ͢΂͖͔൱͔ʙ w.JDSPTFSWJDFTͷηΦϦʔ  ֶͼ͍ͨํ͸

Slide 3

Slide 3 text

ࠓճ࿩͢͜ͱ w ΫοΫύουͰͷࣄྫ ‣ ͦ΋ͦ΋Ͳ͏͍͏ཻ౓Ͱ෼͔Ε͍ͯΔʁ ‣ αʔϏεؒ͸ͲͷΑ͏ʹ௨৴͢Δʁ ‣ αʔϏεؒͷςετͬͯͲ͏΍ͬͯΔʁ ‣ ߏ੒؅ཧʗσϓϩΠ͸ʁԾ૝Խͯ͠Δʁ ‣ ϏϧυύΠϓϥΠϯ͸ʁόονδϣϒ͸ʁ ‣ νʔϜؒͷ࿈ܞ΍ҙࢥܾఆ͸Ͳ͏ͯ͠Δʁ 

Slide 4

Slide 4 text

ͲΜͳαʔϏε͔Β ߏ੒͞Ε͍ͯΔ͔ 

Slide 5

Slide 5 text

αʔϏεͷओͳ෼ྨ wϢʔβʔαʔϏε ‣ Ϣʔβʔ͕௚઀࢖͏ϓϩμΫτ wϏϡʔαʔϏε ‣ ͋ΔαʔϏεͷผϏϡʔ ‣ ͍ΘΏΔ#BDLFOEGPSGSPOUFOET wڞ௨ج൫ ‣ ܾࡁɺ௨஌ɺϩάFUDʜ 

Slide 6

Slide 6 text

ϢʔβʔαʔϏε 

Slide 7

Slide 7 text

ϢʔβʔαʔϏε  wϓϩμΫτʹϑΥʔΧεͨ͠λΠϓ ྉཧڭࣨ Ϩγϐ Ϣʔβʔ ΧϑΣ ܾࡁ ௨஌ ಠཱͨ͠αʔϏε Α͋͘Δػೳ͸ ڞ௨ج൫Λ׆༻ ଞͷυϝΠϯͷϞσϧΛར༻Ͱ͖Δ

Slide 8

Slide 8 text

ϏϡʔαʔϏε  wυϝΠϯϞσϧ͸ಉ͡ͰผͷϏϡʔ OEM൛ Ϩγϐ Ϩγϐ Ϣʔβʔ ௨஌ ࠜຊతʹఏڙ͢Δ΋ͷ͸ಉ͡ UI΍ΞΧ΢ϯτମܥ͕ҧ͏ ඞཁʹԠͯ͡ଞαʔϏε Λ૊Έ߹ΘͤΔ ΦϦδφϧͷυϝΠϯ

Slide 9

Slide 9 text

ϏϡʔαʔϏε w#'' #BDLFOEGPSGSPOUFOET ʹ͍ۙ ‣ Α͋͘ΔσόΠε͝ͱʹઐ༻ϏϡʔΛ࡞Δ έʔε͸΄΅ͳ͍ ‣ 0&.൛ͷΑ͏ͳέʔεͰ࢖ΘΕ͍ͯΔ  ఏڙ͢ΔυϝΠϯϞσϧ͸ಉ͡ 㾎ҟͳΔ6*Ͱఏڙ͢Δ 㾎ΞΧ΢ϯτମܥ͕ҧ͏ 

Slide 10

Slide 10 text

ڞ௨ج൫αʔϏε wڞ௨ͯ͠࢖ΘΕΔج൫ػೳ w 0"VUIϓϩόΠμ w ܾࡁ w 1VTI௨஌ʗϝʔϧ഑৴ w ΞΫςΟϏςΟ w ϩά w ηΩϡΞσʔλ 

Slide 11

Slide 11 text

αʔϏεؒ͸ͲͷΑ͏ʹ ௨৴͢Δͷ͔ 

Slide 12

Slide 12 text

3&45GVM)ZQFSNFEJB"1* wύϑΥʔϚϯεΑΓॊೈੑʗૄ݁߹Λॏࢹ ‣ Ϟσϧ͕ඇৗʹଟؔ͘࿈͕ෳࡶ ‣ ಛఆͷ௒ߴෛՙϞσϧ͕͋ΔΘ͚Ͱ͸ͳ͍  { "id": 123, "name": "ύυඒ", "kitchen": { "id": 15, "created": "2016-03-10T10:59:24+09:00", } }, "_links": { "self": { "href": "/v1/users/123" }, "recipes": { "href": "/v1/users/123/recipes" }, } }

Slide 13

Slide 13 text

(BSBHF w HJUIVCDPNDPPLQBEHBSBHF w 3&45GVMIZQFSNFEJB"1*GPS3BJMT  class Employee < ActiveRecord::Base include Garage::Representer belongs_to :division has_many :projects property :id property :title property :division, selectable: true collection :projects, selectable: true link(:division) { division_path(division) } link(:projects) { employee_projects_path(self) } def self.build_permissions(perms, other, target) perms.permits! :read end end

Slide 14

Slide 14 text

(BSBHF$MJFOU w HJUIVCDPNDPPLQBEHBSBHF@DMJFOU w YYYDMJFOU͕ແݶ૿৩͠ͳ͍ w ͲͷαʔϏεΛར༻͢Δͱ͖΋ಉ͡ΠϯλʔϑΣΠε w αʔϏε͝ͱʹҟͳΔͱֶशίετ͕ߴ͍  # GET https://garage.example.com/v1/me user = client.get("/me") user.id user.name # GET https://garage.example.com/v1/recipes recipes = client.get("/recipes") recipes.total_count recipes[0].id recipes[0].name

Slide 15

Slide 15 text

αʔϏεؒ௨৴ͷ ίετܰݮʗ଱ো֐ੑ 

Slide 16

Slide 16 text

&YQFEJUPS wHJUIVCDPNDPPLQBEFYQFEJUPS w3VCZ൛ͷ/FUqJY)ZTUSJY w"TZODISPOPVTFYFDVUJPO wෳ਺ͷαʔϏε͔ΒͷϦιʔεΛฒߦͯ͠औಘ w'BVMUUPMFSBODF w$JSDVJUCSFBLFS 

Slide 17

Slide 17 text

αʔϏεؒ࿈ܞͷςετ 

Slide 18

Slide 18 text

ޓ׵ੑΛͲ͏୲อ͢Δ͔ w αʔϏε͝ͱͷςετ͸͞Ε͍ͯΔ w ࿈ܞઌ͸ελϒ͢Δ w ඇޓ׵ͷมߋʹؾ෇͚ͳ͍ w ܕPSςετ w +40/4DIFNB͸؅ཧ͕େมͦ͏ w ΋ͱ΋ͱςετจԽ͸ࠜ෇͍͍ͯͨͷͰ্ख͘ ׆༻͍ͨ͠ 

Slide 19

Slide 19 text

$POTVNFS%SJWFO$POUSBDUUFTUJOH wར༻ଆ $POTVNFS ͕"1*Λελϒͯ͠ςετ ‣ ظ଴͢ΔৼΔ෣͍Λܖ໿ $POUSBDU ͱͯ͠ "1*ଆʹ౉͢ w"1*ଆ͸ࣗ਎͕$POUSBDUΛຬ͍ͨͯ͠Δ͔Ͳ͏ ͔ݕূʢςετʣ͢Δ  http://martinfowler.com/articles/consumerDrivenContracts.html

Slide 20

Slide 20 text

1BDU w HJUIVCDPNSFBMFTUBUFDPNBVQBDU w ΫϥΠΞϯτଆ w $*ͰQBDUϑΝΠϧΛੜ੒ w "1*ଆ w $*ͰQBDUϑΝΠϧΛWFSJGZ  ग़య: https://github.com/realestate-com-au/pact#how-does-it-work

Slide 21

Slide 21 text

1BDU w ΫϥΠΞϯτଆ  describe 'get_all' do let(:recipe_a) { { id: Pact.like(1), name: Pact.like('Curry') } } let(:recipe_b) { { id: Pact.like(2), name: Pact.like('Salada') } } before do provider_app.given('there are 2 recipes'). upon_receiving('a request for recipes'). with(method: :get, path: '/v1/recipes'). will_respond_with( status: 200, headers: { 'Content-Type' => Pact.term( generate: 'application/json', matcher: %r{application/json} ), }, body: [recipe_a, recipe_b] ) end it 'returns recipes' do recipes = described_class.get_all expect(recipes.size).to eq(2) expect(recipes.first.name).to eq('Curry') end end

Slide 22

Slide 22 text

1BDU w QBDUpMFʢҰ෦ʣ  { "consumer": { "name": "ConsumerApp" }, "provider": { "name": "ProviderApp" }, "interactions": [ { "description": "a request for recipes", "provider_state": "there are 2 recipes", "request": { "method": "get", "path": "/v1/recipes" }, "response": { "status": 200, "headers": { "Content-Type": { "json_class": "Pact::Term", "data": { "generate": "application/json", "matcher": { "json_class": "Regexp", "o": 0, "s": "application/json" } }

Slide 23

Slide 23 text

1BDU w "1*ଆ w ඞཁͳલॲཧ͚ͩ༻ҙ͢Δ  Pact.provider_states_for 'ConsumerApp' do provider_state "there are 2 recipes" do set_up do %w[Curry Salada].each {|name| Recipe.create!(name: name) } end end end

Slide 24

Slide 24 text

3BDL7$3 w HJUIVCDPNNJZBHBXBSBDLWDS w 7$3͕ϕʔεͰखܰ w "1*ϦΫΤετΛΧηοτσʔλԽͯ͠ελϒ w "1*ଆ͕ΧηοτσʔλΛΫϥΠΞϯτʹఏڙ͢Δ w ඇޓ׵ͷมߋ͕͋Ε͹ΫϥΠΞϯτଆͷ$*Ͱݕ஌ w "1*ଆͷϏϧυͷํ͕ߴස౓ͷ৔߹ɺΫϥΠΞϯτଆͷ ݕ஌લʹϦϦʔε͞Ε͏Δ w ݱࡏ͸1BDUʹҠߦͨ͠ 

Slide 25

Slide 25 text

αʔϏεΛ·͍ͨͩ ΤϥʔΛ௥੻͢Δ 

Slide 26

Slide 26 text

4FOUSZ w HJUIVCDPNHFUTFOUSZTFOUSZ w αʔϏε͝ͱͷΤϥʔΛू໿؅ཧ͍ͯ͠Δ w ͜Ε͚ͩͩͱαʔϏεؒͷඥ෇͚͕Ͱ͖ͳ͍ w αʔϏε"ˠ#΁ͷϦΫΤετ࣌ʹΤϥʔ w Τϥʔ͸"ͱ#ͦΕͧΕͰه࿥͞ΕΔ w ͜ͷো֐ʹΑΔΤϥʔͷൣғ͸ʁ 

Slide 27

Slide 27 text

5SBDF-PHHJOH w ϦΫΤετ*% w ϔομʹ͋Ε͹ͦͷ··࢖͍ɺͳ͚Ε͹࠾൪ w (BSBHF$MJFOU͕ϦΫΤετ࣌ʹηοτ w ϦΫΤετઌʹ఻೻ w 4FOUSZ w ϦΫΤετ*%ͰΤϥʔΛݕࡧ 

Slide 28

Slide 28 text

αʔόʔʗ࣮ߦ؀ڥʗߏ੒؅ཧ 

Slide 29

Slide 29 text

%PDLFS w ϙʔλϒϧͳ࣮ߦ؀ڥ w TUBHJOHQSPEVDUJPOόονಉ͡Πϝʔδ w ؀ڥґଘ஋͸؀ڥม਺ͱͯ͠஫ೖ w ຊମͷڊେ3BJMTͳͲҰ෦Λআ͖ɺ΄ͱΜͲͷΞϓϦέʔ γϣϯ͕%PDLFS w ΞϓϦέʔγϣϯͷϦϙδτϦʹ%PDLFSpMF w ։ൃऀ͕खݩͰ࿔ΕΔΑ͏ʹ w ϖωτϨςετ؀ڥͳͲඞཁʹԠ͙ͯ͢͡࡞ΕΔ 

Slide 30

Slide 30 text

ϏϧυύΠϓϥΠϯ w$*ͰΞϓϦέʔγϣϯͷςετ w%PDLFSΠϝʔδ࡞੒ wBTTFUQSFDPNQJMFࡁΈͷΠϝʔδ wTUBHJOHʹࣗಈσϓϩΠ wQSPEVDUJPO͸DIBUPQTͰσϓϩΠ 

Slide 31

Slide 31 text

)BLP w HJUIVCDPNFBHMFUNUIBLP w %PDLFSΞϓϦ༻ͷσϓϩΠπʔϧ w ΫοΫύουͰ͸όοΫΤϯυʹ&$4Λར༻ w ΞϓϦέʔγϣϯ͝ͱͷઃఆ͸:".-Ͱ؅ཧ w ؀ڥ͝ͱʹ؀ڥม਺ΛઃఆͰ͖Δ w ൿಗ஋͸FUDFOWFUDWBVMUͰ؅ཧ 

Slide 32

Slide 32 text

FUDFOWFUDWBVMU w HJUIVCDPNTPSBIFUDFOW w FUDEͰઃఆ஋Λ؅ཧ w HJUIVCDPNTPSBIFUDWBVMU w FUDEͷ஋Λ҉߸Խ w ΋ͱ΋ͱFUDEʹ"$-͕ແ͔ͬͨͨΊ࡞ΒΕͨ w ࢦఆͷ伴Λ͍࣋ͬͯΔΠϯελϯεͰ͔͠෮߸ Ͱ͖ͳ͍ 

Slide 33

Slide 33 text

FUDXFC w HJUIVCDPNTPSBIFUDXFC 

Slide 34

Slide 34 text

DIBUPQT w )VCPUͰσϒϩΠ w σϓϩΠδϣϒ͸3VOEFDLͰ؅ཧ w σϓϩΠํ๏͕౷Ұ͞Ε͍ͯΔ w νϟοτݟͯΔͱΘ͔Δ 

Slide 35

Slide 35 text

όονδϣϒ 

Slide 36

Slide 36 text

,VSPLP wશαʔϏεͷδϣϒΛҰׅ؅ཧ ‣ ϫʔΫϑϩʔ؅ཧ  ผαʔϏεͷδϣϒΛϑοΫ͢Δ ‣ 8FCίϯιʔϧͰΤϥʔ֬ೝɾ࠶࣮ߦ 

Slide 37

Slide 37 text

,VSPLP wαʔϏεͷ࠷৽ͷ%PDLFSΠϝʔδΛ࣮ߦ ‣ αʔϏε͝ͱͷϫʔΧʔ؀ڥෆཁ ‣ Ҏલ͸֤ΞϓϦͰLVSPLPઐ༻ͷߏ੒؅ཧΛ ༻ҙ͍ͯͨ͠ ‣ ֤αʔϏεͷΞϓϦέʔγϣϯ͕ͦͷ··ಈ ͘ 

Slide 38

Slide 38 text

νʔϜ͝ͱͷαϙʔτ νʔϜΛԣஅͨ͠ҙࢥܾఆ 

Slide 39

Slide 39 text

ٕज़ྖҬ՝୊ڞ༗ձ w֤νʔϜͷٕज़Ϧʔμʔͱٕज़ج൫ʴΠϯϑϥ Ͱٕज़తͳ՝୊ʹ͍ͭͯ࿩͠߹͏ w։ൃ͢Δͱ͖ʹࠔ͍ͬͯΔ͜ͱ w͋ͬͨΒخ͍͠΋ͷɺ΋͏ཁΒͳ͍΋ͷ wΈΜͳ͕ࠔ͍ͬͯΔʗඞཁͱ͍ͯ͠Δͷ͔ɺ ͦͷνʔϜ͚ͩͳͷ͔ 

Slide 40

Slide 40 text

ٕज़ج൫୲౰ w ֤νʔϜͷٕज़తͳࠩ w ٕज़ج൫ʹ·Θ͢༨ྗ͕͋Δʗͳ͍ w ج൫ܥͷܦݧ͕͋Δϝϯόʔ͕͍Δʗ͍ͳ͍ w ֤νʔϜʹઐ೚ͷٕज़ج൫୲౰ w ௨ৗͷґཔͱ͸ผʹΧδϡΞϧʹ૬ஊͰ͖Δ w ͕ͬͭΓೖͬͯվળ͢Δ͜ͱ΋ 

Slide 41

Slide 41 text

·ͱΊ w αʔϏεߏ੒ ‣ ϓϩμΫτϑΥʔΧεʗ#''ʗڞ௨ج൫ w αʔϏεؒͷ࿈ܞ ‣ 3&45GVM)ZQFSNFEJB"1*(BSBHF ‣ $%$UFTUJOH1BDU w%PDLFSΛத৺ͱͨ͠؀ڥ ‣ )BLP &$4 FUDFOWFUDWBVMU ,VSPLP wνʔϜ࿈ܞʗαϙʔτ ‣ ٕज़ྖҬ՝୊ڞ༗ձɺٕज़ج൫୲౰