Upgrade to PRO for Only $50/Year—Limited-Time Offer! 🔥

マイクロサービス指向 Rails API 開発ガイド/building rails api o...

qsona
August 05, 2017

マイクロサービス指向 Rails API 開発ガイド/building rails api on microservices

2017/8/5 ぎんざRuby会議01 @みんなのウェディング

引用のリンク先などはこちらを参照ください
https://medium.com/finc-engineering/ginzaruby01-rails-api-guide-168fe9cf5b4d

qsona

August 05, 2017
Tweet

More Decks by qsona

Other Decks in Technology

Transcript

  1. ϚΠΫϩαʔϏεࢦ޲ Rails API ։ൃΨΠυ ৿ ٱଠ࿠ (@qsona), גࣜձࣾFiNC 2017/8/5 ͗Μ͟Rubyձٞ01

    @ΈΜͳͷ΢ΣσΟϯά ৿ ٱଠ࿠ (@qsona) גࣜձࣾFiNC 2017/8/5 ͗Μ͟Rubyձٞ01 @ΈΜͳͷ΢ΣσΟϯά
  2. whoami • 2013- Node.js ͰͷήʔϜ։ൃ(αʔόαΠυ) • 2016/2- גࣜձࣾFiNCॴଐ • Ruby

    on Rails, Microservices Architecture • ࣾྺ=Railsྺ=1೥൒
  3. whoami • 2013- Node.js ͰͷήʔϜ։ൃ(αʔόαΠυ) • 2016/2- גࣜձࣾFiNCॴଐ • Ruby

    on Rails, Microservices Architecture • ࣾྺ=Railsྺ=1೥൒ ͓ͯ΍ΘΒ͔ʹʂʂ
  4. ࠓ೔ͷτϐοΫ • ϚΠΫϩαʔϏε • Ruby on Rails • Web API

    ͲΕ΋FiNCͰͷ։ൃͰ
 େࣄʹ͍ͯ͠Δ΋ͷɻ
  5. ࠓ೔ͷΞδΣϯμ 1. Ұ؏ੑͷ͋ΔWeb APIઃܭ
 ϚΠΫϩαʔϏεࢦ޲։ൃͷࢹ఺Λަ͑ɺWeb APIઃܭΛٞ࿦͠·͢ɻ 2. Rails API։ൃͷϓϥΫςΟε
 ্هͷWeb

    APIઃܭʹର͠ɺRailsͰͷ࣮૷දݱΛ༩͍͖͑ͯ·͢ɻ 3. ੍໿͔Β֎ΕͨAPIΛ࡞Δ࣌
 ͜͜·ͰͰ࿩ͨ͜͠ͱͷྫ֎ʹͳΔΑ͏ͳέʔεΛߟ͑ͯΈ·͢ɻ
  6. Ϧιʔεͷྫ // GET /v1/users/1 { // User Ϧιʔε "id": 123,

    "name": "qsona", "age": 17, "jobs": [ // Job Ϧιʔεͷ഑ྻ { "id": 2, "name": "ձࣾһ" }, { "id": 7, "name": "ࣗ୐ܯඋһ" } ] }
  7. APIઃܭͰେମղܾͰ͖Δ • Ϧιʔεͷཻ౓Λɺࡉ͔͗͘͢͠ͳ͍͜ͱ • ΞϓϦέʔγϣϯ্ͷυϝΠϯϞσϧͷཻ౓ • ςʔϒϧͷ୯ҐͰ͸ͳ͍ • ϦΫΤετͷ N+1

    ໰୊͕ى͜Δͷ͸ɺϦιʔεͷཻ౓ͷϛε=APIઃܭϛ εͰ͋Δ͜ͱ͕΄ͱΜͲ • ͱ͸͍͑ɺຊདྷؔ܎ͷͳ͍ϦιʔεΛ1ը໘Ͱෳ਺ར༻ͨ͠Γɺ
 αʔϏεʹ·͕ͨͬͯσʔλΛऔಘ͍ͨ࣌͠΋͋Δ
  8. BFF (Backends For Frontends) • ϑϩϯτΤϯυͷͨΊͷαʔόʔ • ΫϥΠΞϯτͱɺόοΫΤϯυαʔϏεͷதؒʹҐஔ ͢Δ •

    ෳ਺ͷϚΠΫϩαʔϏεͷAPIΛɺฒྻ/௚ྻͰୟ͖ɺ ϦιʔεΛ·ͱΊΔ໾໨Λ࣋ͭ (API Aggregation)
  9. RESTfulͷܗଶ Richardson Maturity Model - Martin Fowler • Ϩϕϧ1: ϦιʔεͱURIͰ

    ͷදݱ • Ϩϕϧ2: HTTPͷಈࢺΛਖ਼ ͘͠࢖͍෼͚Δ • Ϩϕϧ3: ϋΠύʔϝσΟΞ ίϯτϩʔϧ (HATEOAS)
  10. RESTful ୈ1ܗଶ • path ͰϦιʔεΛදݱ͢Δ • ex) GET /v1/users/:user_id •

    User Ϧιʔε • ex) GET /v1/users/:user_id/jobs • User Ϧιʔεʹैଐ͢Δ Job Ϧιʔεͷ഑ྻ • Ϧιʔεͷैଐؔ܎͕Θ͔Γ΍͍͢
  11. RESTful ୈ2ܗଶ • HTTPͷಈࢺ (GET, POST, ...) Λར༻͢Δ • ex)

    POST /v1/users ... Ϣʔβ࡞੒ • HTTPඪ४ʹଇΔϝϦοτ͕͋Δ • pathʹಈࢺΛؚΊͳ͍ͱ͍͏੍໿͕ɺϦιʔ εΛߟ࡯͢Δ͜ͱΛଅ͢
  12. RankingUser#index API • RankingUser Ϧιʔε • score, rank • user_id

    • ranking_idͱɺظؒ(today/total) Λ ύϥϝʔλͰड͚Δ
  13. UserProfile API • UserProfile Ϧιʔε • name, image • ෳ਺ͷ

    user_id ΛύϥϝʔλͰड ͚ͯɺ഑ྻͰฦ͢ • ͜ͷAPI͚ͩ͸ɺϝΠϯαʔϏε ʹଐ͍ͯ͠Δ
  14. BFFͰू໿͢Δ 1. Ranking औಘ 2. Ranking id͔ΒɺRanking UserΛ ഑ྻͰऔಘ 3.

    Ranking Userͷ user_id഑ྻ͔Βɺ UserProfile Λ഑ྻͰऔಘ
  15. υϝΠϯͱͯ͠ͷߟ࡯ • ʮλϒʯΛυϝΠϯͱͯ͠ଊ͑ͯΈΔ • Ranking has_many Tabs • Tab has_many

    RankingUsers • Tab͸ɺRankingUserͷू߹ͷऔΓํΛ࣋ͭ • ΫϥΠΞϯτΤϯδχΞͱ͜ͷઃܭͷٞ࿦Λ ߦ͍ɺ2࣌ؒΛඅ΍ͨ͠ (ઃܭॏཁ)
  16. Ϧιʔεࢦ޲ͱͷରԠ • Serializer Ϋϥε͕ APIͷϦιʔεʹରԠ͢Δ • 1ͭͷAPIͰɺModelΛ1͚ͭͩγϦΞϥΠζ͢Δ • γϦΞϥΠβ͸Ҿ਺ΛऔΒͳ͍ •

    શͯͷ৘ใ͕1ͭͷModelʹೖ͍ͬͯΔ • instance_options (ͱ͍͏ active_model_serializersͷػೳ) ͸࢖Θͳ͍
  17. RESTful (ୈ2ܗଶ·Ͱ) ͷදݱ • Rails Wayʹ৐Δ • routes / ActionController

    • pathͱControllerઃܭ • ύϥϝʔλ͔ΒϞσϧΛҾ͘ίʔυΛڞ௨Խ͢Δ • DHHͷControllerઃܭ
  18. class V1::UsersController < ApplicationController def show render json: user, serializer:

    V1::UserSerializer end 
 def jobs render json: user.jobs, each_serializer: V1::JobSerializer end def parent render json: user.parent, serializer: V1::UserSerializer end private def user @user ||= User.find(params[:user_id]) end end
  19. DHHͷControllerઃܭ • Controller ʹఆٛ͢ΔΞΫγϣϯ͸جຊ7ΞΫγϣϯͷΈ • APIͰ͸5ΞΫγϣϯ (index, show, create, update,

    destroy) • ͦΕҎ֎Λఆٛͨ͘͠ͳͬͨΒɺίϯτϩʔϥΛ෼ׂ͢Δ How DHH Organizes His Rails Controllers , Jerome Dalbert
 ೔ຊޠ༁ DHH͸ͲͷΑ͏ʹRailsͷίϯτϩʔϥΛॻ͘ͷ͔ , POSTD
  20. class V1::UsersController < ApplicationController include V1::Users::ControllerConcern def show render json:

    user, serializer: V1::UserSerializer end end class V1::Users::JobsController < ApplicationController
 include V1::Users::ControllerConcern def index render json: user.jobs, each_serializer: V1::JobSerializer end end class V1::Users::ParentsController < ApplicationController include V1::Users::ControllerConcern def show render json: user.parent, serializer: V1::UserSerializer end end /v1/users/:user_id /v1/users/:user_id/jobs /v1/users/:user_id/parent
  21. module V1::Users::ControllerConcern def user(includes: nil) @user ||= if includes User.includes(includes).find(params[:user_id])

    else User.find(params[:user_id]) end end end path ͔ΒϞσϧΛ औಘ͢Δ෦෼Λڞ ௨Խ͢ΔͨΊͷ Controller Concern
  22. // ͋ΔϥϯΩϯάΛऔಘ͢ΔAPIͷϨεϙϯεϘσΟ { "id": 123, "name": "Rails Contributors from Japan",

    "my_score": 0, "my_rank": null, "ranking_users": [ { "name": "kamipo", "rank": 1, "score": 811 }, { "name": "a_matsuda", "rank": 2, "score": 700 }, { "name": "y-yagi", "rank": 3, "score": 532 } ] }
  23. // ͋ΔϥϯΩϯάΛऔಘ͢ΔAPIͷϨεϙϯεϘσΟ { "id": 123, "name": "Rails Contributors from Japan",

    "my_score": 0, "my_rank": null, "ranking_users": [ { "name": "kamipo", "rank": 1, "score": 811 }, { "name": "a_matsuda", "rank": 2, "score": 700 }, { "name": "y-yagi", "rank": 3, "score": 532 } ] } ͚ͩ͜͜ɺೝূϢʔβࢹ఺ͰͷϥϯΩϯά৘ใ
  24. Ranking (ActiveRecord) # == Schema Information # id :bigint #

    name :string class Ranking < ApplicationRecord has_many :ranking_users end
  25. UserRanking (non-AR Model) class UserRanking include ActiveModel::Model include ActiveModel::Serialization attr_accessor

    :user, :ranking delegate :id, to: :ranking delegate :name, to: :ranking delegate :ranking_users, to: :ranking def my_score ranking_users.find_by(user_id: user.id)&.score end end
  26. UserRanking (non-AR Model) • Ranking + User ͔ΒͳΔϞσϧ • Ranking

    ͷϝιου͸delegate͍ͯ͠Δ • ໘౗ͩͬͨΒmethod_missingͰ... • "User͔ΒݟͨRanking" ͷϝιουΛ௥Ճ͍ͯ͠Δ • my_score / my_rank
  27. V1 UserRanking Serializer • UserRankingͷΠϯελϯεΛγϦΞϥΠζ͢Δ • APIʹఆٛ͢ΔϦιʔε͸ɺ͜ΕͱରԠ͍ͯ͠Δ class V1::UserRankingSerializer <

    ActiveModel::Serializer attributes :id, :name, :my_score, :my_rank has_many :ranking_users, serializer: V1::RankingUserSerializer end
  28. V1 Users Rankings Controller • ϧʔςΟϯά͸ GET /v1/users/me/rankings/:id ͱͨ͠ •

    `me` ͸ೝূ͞ΕͨϢʔβΛද͢ɺuser_idͷΤΠϦΞε • ࢦఆͨ͠Ϣʔβࢹ఺ͰͷϥϯΩϯά͕ཉ͍͠ͱ͖͸ 
 GET /v1/users/:user_id/rankings/:id • ϚΠΫϩαʔϏεؒͰAPIΛར༻͢Δͱ͖͸ɺͪ͜Β͕࢖ΘΕΔ
  29. # app/controllers/v1/users/rankings_controller.rb class V1::Users::RankingsController < ApplicationController def show user_ranking =

    UserRanking.new(ranking: ranking, user: user) render json: user_ranking, serializer: V1::UserRankingSerializer end private def ranking @ranking ||= Ranking.find(params[:id]) end def user @user ||= if params[:user_id] == 'me' current_user! else User.find(params[:user_id]) end end end
  30. # app/controllers/v1/users/rankings_controller.rb class V1::Users::RankingsController < ApplicationController def show user_ranking =

    UserRanking.new(ranking: ranking, user: user) render json: user_ranking, serializer: V1::UserRankingSerializer end private def ranking @ranking ||= Ranking.find(params[:id]) end def user @user ||= if params[:user_id] == 'me' current_user! else User.find(params[:user_id]) end end end params ͔ΒऔΕΔσʔλ͸
 privateϝιουʹ·ͱΊΔ
  31. # app/controllers/v1/users/rankings_controller.rb class V1::Users::RankingsController < ApplicationController def show user_ranking =

    UserRanking.new(ranking: ranking, user: user) render json: user_ranking, serializer: V1::UserRankingSerializer end private def ranking @ranking ||= Ranking.find(params[:id]) end def user @user ||= if params[:user_id] == 'me' current_user! else User.find(params[:user_id]) end end end params ͔ΒऔΕΔσʔλ͸
 privateϝιουʹ·ͱΊΔ render ʹ௚઀ ϞσϧΛ౉͢
  32. 2ষ ·ͱΊ • ڧ͍Ϧιʔεࢦ޲ΛRailsͰकΔʹ͸ɺͦΕʹ߹ͬͨγϦΞ ϥΠβ (ྫ͑͹ active_model_serializers) ΛબͿ • ϦιʔεʹରԠ͢ΔϞσϧ

    (AR/non-AR) Λ࡞Δ • RESTfulΛकΔʹ͸ɺRails wayʹ৐͍͚ͬͯ͹͍͍ • Controller ͷ࢖͍ํΛ޻෉͠ɺpathͷղऍΛڞ௨Խ͢Δ
  33. 1) ϔομ೿ Best Practices for Designing a Pragmatic RESTful API

    , Vinay Sahni
 ຋༁: WebAPI ઃܭͷϕετϓϥΫςΟε , mserizawa
  34. // GET /v1/users [ { "id": 1, "name": "kamipo" },

    { "id": 2, "name": "a_matsuda" }, { "id": 3, "name": "y-yagi" }, { "id": 4, "name": "willnet" }, { "id": 5, "name": "ken1flan" } ] // GET /v1/users/paging?cursor=2&limit=2 { "cursor": 4,
 "data": [ { "id": 3, "name": "y-yagi" }, { "id": 4, "name": "willnet" } ] } GET /v1/users/ ϢʔβҰཡɻ
 ϖʔδϯά͸ͳ͍ GET /v1/users/paging ϖʔδϯά͋Γͷ
 ϢʔβҰཡɻ
  35. ڧ͍Ϧιʔεࢦ޲Λద༻ͨ͠ • ϧʔϧΛγϯϓϧͳ··อͭ͜ͱʹ੒ޭͨ͠ • ͜ΕͰࠔ͍ͬͯͳ͍͠ɺ࣮ࡍʹӡ༻Ͱ͖͍ͯΔ • index ͱ paging Ͱ͸ҧ͏ϦιʔεΛฦ͍ͯ͠ΔͷͰɺ


    index Ͱ͸τοϓϨϕϧͷ഑ྻɺ
 paging Ͱ͸ dataҎԼʹ഑ྻɺͱ֊૚͕มΘͬͯΔͷ͸ҧ࿨ײͳ͍ • ޙ͔Βϖʔδϯά͕ඞཁʹͳͬͨΒɺͦΕ͸΋͏ޙํޓ׵ੑ͕ͳ͘ ͳΔมߋͱଊ͑ͯɺURIม͑ͨ΄͏͕͍͍ͱׂΓ੾͍ͬͯΔ
  36. ଞʹ΋৭ʑ͋Δ • ex) POSTܥͷAPIͰɺ෭࡞༻ͷ݁ՌΛฦ͍ͨ͠ͱ͖͸? • ex) ύϑΥʔϚϯεʹ೉͕͋ΓɺBFFͰ΋ղܾͰ͖ͳ͍ͱ͖͸? • ΋ͪΖΜʮۜͷ஄ؙ͸ͳ͍ʯ •

    ϧʔϧ͸γϯϓϧͳํ͕͍͍͕ɺͲ͜·Ͱ੍໿Λద༻Ͱ͖Δ͔͸ ΞϓϦέʔγϣϯͷੑ࣭࣍ୈ • ྑ੍͍໿Λϕʔεʹɺ޻෉ͯ͠࠷దղΛݟ͚͍͖ͭͯ·͠ΐ͏
  37. ࠓ೔࿩ͤͳ͔ͬͨ͜ͱ • APIͷఆٛͱɺυΩϡϝϯτͷࣗಈੜ੒ • FiNCͰ͸ JSON Hyper Schema / prmd,

    committee Λར༻͍ͯ͠Δ • committee + prmd ͰJSON SchemaΛ͍͍ײ͡ʹӡ༻͢Δ - ͓͓ͨͷ෺ஔ ( @ota42y ) • ࠓ೔࿩ͨ͠Α͏ͳAPI։ൃελΠϧΛखʹೖΕΔ·ͰͷಓͷΓ • FiNCͷWeb API։ൃࣄ৘ - Fumiya Shinozuka