『WEB+DB PRESS Vol.108』でスキーマ駆動Web API開発についての特集記事を執筆しました

このたび、2018年12月22日に発売された『WEB+DB PRESS Vol.108』の特集1「効率急上昇!スキーマ駆動Web API開発」の企画と執筆に携わりました。id:june29さん、id:ackintoshさん両名との共著です。

WEB+DB PRESS Vol.108

WEB+DB PRESS Vol.108

  • 作者: 中野暁人,山本浩平,大和田純,曽根壮大,ZOZOTOWNリプレースチーム,権守健嗣,茨木暢仁,松井菜穂子,新多真琴,laiso,豊田啓介,藤原俊一郎,牧大輔,向井咲人,大島一将,上川慶,末永恭正,久保田祐史,星北斗,池田拓司,竹馬光太郎,粕谷大輔,WEB+DB PRESS編集部
  • 出版社/メーカー: 技術評論社
  • 発売日: 2018/12/22
  • メディア: 単行本
  • この商品を含むブログを見る

Web APIの仕様を表現するフォーマット(スキーマ)を用いたWeb API開発について、基礎的なところから紹介しています。本号はこの特集の他にもPostgreSQLやZOZOのシステムリプレイスなど、非常に興味深い内容が盛りだくさんなので、ご興味のある方はぜひ手にとっていただければと思います。

以上、宣伝でした。


以下、裏話的なことを書きます。

なぜ書いたのか

そもそも現在業務として携わっているWebサービスの開発でOpenAPI(いわゆるSwagger)を利用しています。ある程度わかっている人にOpenAPIについて知ってもらうにはWeb上にある次のようなリソースが役立ちます。

一方、新しくチームに入った人やそこまでWeb API開発に詳しくない人に対して、OpenAPIについて「読んどいて!」と言える文献があると、自分自身はもちろん、同じ境遇の他の人にも便利なものになるのではと感じていました。そして、REST APIのスキーマに関して最も有力な選択肢はOpenAPIなので、その説明を含めた文献があるとよさそうだと考えました。

また、これは執筆を始めたあとの話ですが、june29さんと「2018年時点でのWeb API開発のスナップショットみたいな記事だね」という話をして、それを意識して書くようにもしていました。

そういうわけで無事に企画が通り、今回寄稿することになったので、社内外で今後OpenAPIについて知ってもらいたいときは積極的にこの特集を勧めていこうと思っています。

補足

今回、OpenAPIに関しては最新版であるバージョン3を前提として、記法や活用方法を解説しました。OpenAPI 3がリリースされたのは2018年6月とまだ新しく、各種ツールのサポートも、Swagger公式やOpenAPI Generator以外の場合は特にですが、まちまちというのが現状です。そういう現状もあって、3章のサーバサイド開発のレスポンスバリデーションに関する部分は、仕組みの解説としてライブラリを使わない素朴な実装方法について書きました。

ここに関しては、今後いろいろなツールでOpenAPI 3がサポートされていくだろうと思うので、とくに主戦場であるRuby/Railsまわりなどは私としても情報発信は継続していきます。また、特集内のコラムで紹介したCommitteeに関しては、@ota42yさんが中心となって対応を進めていただいているので、私自身も試しつつ貢献できる部分はしていきたいと思っています。

また、スキーマの活用という点で、Protocol Buffersからのサーバ/クライアント生成が特徴的なgRPCをなぜ取り上げていないのか、という点は指摘としてあるかなと思います。ここに関しては、今回の執筆陣がgRPCに関する知見をそれほど持っておらず書かなかったというのが正直なところです。最近はgRPC-WebなどブラウザからgRPCのサービスをコールできる技術も出てきているようなので、詳しい知見を持つ方々にぜひ執筆してほしいという気持ちです…!

おわりに

よろしくお願いします!

Stripe Sources APIにおける決済の抽象化

この記事はGMOペパボ Advent Calendar 2018の23日目の記事です。

GMOペパボのAdvent Calendarと言っておきながら、Stripeが提供しているWeb APIの話をします。

Stripeの決済用Web APIでは、sourceという概念を通じて多様な決済方法を抽象化して表現しています。このsourceの概念がEC系Webサービス*1を開発している人間としては興味深いものだったので紹介します。

sourceとは

Stripe公式のリソースは次のあたりのものです。

stripe.com

stripe.com

sourceは多様な決済方法を取り扱うために導入されている概念です。sourceの状態がchargeable(課金可能)になると、買い手に対してcharge(課金)することができます。

もう少し詳しく説明すると、sourceは買い手が決済するために使う「モノ」を抽象化した概念であると言えます。その具象としては、クレジットカードやACH(銀行振込のようなもの)があります。1回きりの決済としてsourceに対して直接課金することもできますし、買い手に対して複数回課金するために買い手にsourceを関連付けておくこともできます。

Stripeのドキュメントとcurlがあれば、sourceを用いたWeb API経由での決済実行をすぐに体験できるので、興味がある人はぜひ触ってみてください(以下リンク先の"Try Now")。

stripe.com

たとえばクレジットカードであれば、JS経由でStripeへ直接カード情報を送信後にsourceのIDを得たあと、次のようにAPIを叩くと買い手へのsourceの関連付けと課金を実行できます。

$ curl https://api.stripe.com/v1/customers -u <key> -d email=<customer email> -d source=<source ID>
$ curl https://api.stripe.com/v1/charges -u <key> -d amount=100 -d currency=usd -d cutomer=<customer ID> -d source=<source ID>

sourceを特徴づける四つの性質

Stripeはsourceに対して次のような四つの性質を定義しています。これらの性質を使ってさまざまなsourceを分類しています。

pull/push

決済時の資金の移動方法です。

  • pull
    • 買い手の同意を得たあとに売り手側が資金を引き落とせる
  • push
    • 買い手側が明示的に資金を送る操作が必要
    • 送金後にsourceがchargeableになる

flow

買い手が決済を承認するための行動フローです。これを通過するとsourceがchargeableになります。

  • none
    • なにもしなくてもOK
  • redirect
    • リダイレクト先の銀行のページなどに遷移して承認する
    • sourceがリダイレクト先URLを持つ
  • code verification
    • 銀行口座など送金元口座の所有者であることを検証する
  • receiver
    • 買い手が送金する
    • sourceに送金先情報を含む

usage

sourceが何回使えるかを表します。

  • reusable
    • 一度flowを通ったあとは追加でchargeできるsource
    • sourceをcustomerに関連付けて、customerに対して課金することで実現
  • single_use
    • 決済するたびに買い手の明示的な同意が必要なsource
    • sourceに対して直接課金する

synchronous/asynchronous

課金の結果がいつ得られるかを表します。

  • synchronous
    • success/failureが即時わかる
  • asynchronous
    • 結果が数日後にわかる
    • sourceの状態はpendingからsuccess/failureに遷移する

決済方法の分類

上述した性質を用いると、sourceの具象である決済方法は次のように分類できます。

  • クレジットカード
    • pull/none/reusable/synchronous
    • 買い手に紐づけておけば、売り手が何度も課金できる
  • ACH(銀行振込のようなもの)
    • push/receiver/reusable/asynchronous
    • 買い手の入金操作が必要、かつ数日かかる(らしい)

その他の決済方法についても、Stripeのドキュメントで表がまとめてあります

sourceの利点

このあたりから私見が入ります。Stripeのsourceの利点は次のようなところだと思います。

  • さまざまな決済方法を同じような扱いかたで扱えるようになる
  • 決済ごとの特徴を決めるための性質を定義することで、新決済に対応しやすくなっている

一つ目については、Stripeのドキュメントを見るとわかるとおり、いろいろな決済方法について利用方法が説明されているのですが、どの決済方法でも概ねsource作成 → sourceへの課金 → Webhookによる結果の確認、という流れになっています。決済が複数あることで複雑化しそうな部分を適切な抽象化で吸収し、統一的なインタフェースとして提供できていることがわかります。

二つ目については、このsourceを用いた決済の抽象化方法を使うと、新しく導入する決済をsourceのインタフェースに載せやすくなります。例えば、Amazon Payや楽天PayのようないわゆるID決済や日本でよく使われる*2後払いについて考えてみます。これらの決済をsourceの性質で分類すると、おおよそ次のようになると思います。

  • ID決済
    • pull/redirect/reusable/synchronous な決済
    • 買い手と外部サービスIDを関連付け、外部サービスに遷移して認証し、あとで追加課金でき(ることが多い)、すぐに決済結果がわかる
  • 後払い
    • push/receiver/single_use/asynchronous な決済
    • 買い手がコンビニなどから明示的に送金し、決済ごとに買い手の承認が必要で、与信に人手を介する場合は結果が出るのに時間がかかる

このように複数の性質を用いて特徴を決定することで、新しい具象に対しても、類似する性質の既存決済と同じフローに載せやすくなっています。

まとめ

StripeのWeb APIにおいて多様な決済をうまく扱うための概念であるsourceについて紹介しました。今回はちょっと間に合わなかったのですが、このようなWeb APIをサーバサイドで実装する際にデータモデリングがどうなるかを考えると、なかなかおもしろいのではと思っています。

なお、id:takatoshionoさんに教えてもらったのがきっかけで、私はこのStripeのWeb APIについて知りました。ありがとうございました!

"The Modular Monolith: Rails Architecture"を読んだ

Modular MonolithというアーキテクチャをRailsアプリケーションへ適用する記事を読みました。

medium.com

モノリスアーキテクチャとマイクロサービスアーキテクチャの中間に位置する、一つのモノリシックなアプリケーション内でドメインごとにモジュールに分解しつつ運用するためのアーキテクチャを、Railsでどのように実装するか、という内容です。

Modular Monolithとは

記事から引用します。

Rather than extracting microservices, we decided to first focus on making our app modular. Our goal was to identify good architectural boundaries before we extracted code out into independent services. This would set us up to be able to migrate to microservices in the future, by having the code structured in a way to make a smooth transition.

  • モノリスから複数のマイクロサービスを抽出するより、まずアプリ内をモジュラーにしていく
  • 独立したサービスとして抽出する前にアーキテクチャ上のよい境界を見つける。スムーズに移行しやすいようにコードを構成しておく

というものです。Railsで実現するための具体的な方法として次の項目が挙げられています。

  • app ディレクトリを持たず、コードはすべて gemsengines の下に置く
  • gems の下に置くコードははRailsに依存しないRubyのコードとなる
    • すべてステートレスであり、ストレージを使わない
    • Active Supportだけは使う
  • engines の下に置くコードはRailsに依存するRubyのコードとなる
    • mountable engineにする
    • Active Recordによってデータを永続化する
    • Action PackによってAPIやWebインタフェースを公開する

なぜModular Monolithを使うのか

rails new してRailsアプリケーションをふつうに作っていくとモノリスアーキテクチャになります。ユーザー管理、商品管理、決済など、さまざまなドメインの機能をまとめて app 配下などで管理しています。一方、ドメインごとにサービスを切り出して別のアプリケーションとして運用し、サービス間はWeb APIなどを通じて連携するマイクロサービスアーキテクチャも存在します。

Martin Fowler氏は、MonolithFirstという記事で、最終的にマイクロサービスにアーキテクチャを移行したいとしても、最初はモノリスから始めるのがよいと述べています。Monolith Firstではアプリケーションを成長させながらドメインの境界を見つけていき、またその必要があるならば、徐々にマイクロサービスとして切り出していきます。

Railsであれば、小〜中規模なアプリケーションならモノリスのままRailsの利点を活かしていくのがよいでしょう。成長して大きくなってきたアプリケーションは、モジュール間の依存が複雑になって変更時の影響範囲が読めなくなってきたりします。こういう場合は、まずモノリスの中をドメインごとにモジュールとして分解していくのが効果的といえ、今回読んだ記事ではModular Monolithを使うのがよいと述べています。

RailsにおけるModular Monolithアーキテクチャの実現

ここからは、記事で説明されているModular Monolithアーキテクチャを実現するための実装方法について、かいつまんで説明します。

コードを gemsengines に配置

前述したように、コードをすべて gemsengines の下に置きます。すべてgemとして構成するので、それぞれのディレクトリにはgemspecが存在します。Gemfileでは次のようにしてロードします。

Dir.glob(File.expand_path("../engines/*", __FILE__)).each do |path|
  gem File.basename(path), :path => path
end

Dir.glob(File.expand_path("../gems/*", __FILE__)).each do |path|
  gem File.basename(path), :path => path
end

やっていることは、enginesgems 配下のディレクトリについて gem メソッドで読み込むgemを指定しています。同一リポジトリ内にgemが存在するので path オプションを使っています。これを見るとわかるように、実質monorepoとなっています。記事では、プロダクトのコアとなるコードとgemのコードを同時に更新できるので、後方互換性が問題とならず便利であることを利点として挙げています。

モジュール構成のスタート地点

記事では、Admin, API, Domain というモジュールから始めたと述べています。AdminAPIDomain にそれぞれ依存する形です。ここから、Domain モジュールを分解していくのがよいだろうと述べています。

境界の遵守

Railsでgemをロードすると、あるクラスは他のクラスに自由にアクセスできてしまいます。これを防ぐために、Railsエンジンのテスト実行時にそのエンジン自体と依存先だけをロードできるようにGemfileの記述を工夫しています。テストはRailsエンジンのディレクトリで実行します。

if ENV["ENGINE"].nil?
  if Dir.pwd.split("/")[-2] == "engines"
    ENV["ENGINE"] = Dir.pwd.split("/").last
  end
end

Dir.glob(File.expand_path("../engines/*", __FILE__)).each do |path|
  engine = File.basename(path)
  gem engine, :path => "engines/#{engine}", :require => (ENV["ENGINE"].nil? || ENV["ENGINE"] == engine)
end

ENGINE 環境変数が設定されていなければ、Railsエンジン名を ENGINE 環境変数に保存しておきます。そして、engines 配下のディレクトリ名をトラバースしながら、ENGINE 環境変数を見て該当のRailsエンジンだけを require しています。この方法によって、あるモジュールが依存先として指定していないモジュールのクラスを使ってしまっているときはテストでエラーにできます。

元記事では、このロード方法をさらに発展させて、変更したgemとRailsエンジンだけテストが実行されるようにしていました。

循環依存の検出

Bundlerのおかげでモジュールの循環依存が検出できるという話です。

ある二つのモジュール間に循環依存があると、それらのモジュールは強く結合しており、実質一つのモジュールになっているといえます。Modular MonolithアーキテクチャでgemやRailsエンジンといったモジュールに分解することで、モジュール間に循環依存があると、それらのモジュールのロード時にBundlerがエラーとして検出してくれるようになります。

疎結合化のためのObserverパターン

Modular Monolithでは、モジュール間の依存方向に気をつける必要があることがわかってきました。モジュール間の依存方向を制御したいときにはObserverパターンが使えます。

# driving_scoreエンジンにある定期ジョブ
score = ScoringService.generate_score(user)
if score.eligible?
  QuoteService.generate_quotes(user)
end

上のコードは quoting (見積)エンジンの持つ QuoteService へ依存が発生しています。driving_score エンジンから quoting エンジンへの依存を作りたくない場合、次のようにすれば、モジュール間の依存を解消して疎結合にできます。

# driving_scoreエンジンにある定期ジョブ
score = ScoringService.generate_score(user)
if score.eligible?
  DRIVING_SCORE_PUB_SUB.publish(:eligible_score, :user_id => user_id)
end

# quotingエンジンにあるイベントsubscribe用コード
DRIVING_SCORE_PUB_SUB.subscribe(:eligible_score) do |user_id|
  QuoteService.generate_quotes(user_id)
end

driving_score エンジンでは eligible_score というイベントだけを発行し、だれがそのイベントを購読しているかに関心はありません。つまり、driving_score エンジンから quoting エンジンへの依存をなくすことに成功しています。

実際には、pub/subの実現にはKafkaやメッセージキュー用のミドルウェアなどが必要になります。私見としては、このあたりはRailsだとWhisperが使えそうだと思いました。

感想

独立したアプリケーションの機能をmountable engineに切り出すと便利というのはこれまでも言われていたことですが*1app 配下を廃してすべてのコードを gemsengines 配下に置くという割り切りや、マイクロサービスを意識して徐々にドメイン境界を見つけて切り出すという点が独特だと思いました。

また、書籍『マイクロサービスアーキテクチャ』では、モノリシックなアプリケーション内にモジュールを作ることは「実世界ではプロセス境界内でのモジュール分離の約束が守られてことはほとんどありません」*2と述べられています。この問題に対しては、Modular Monolithの設計方針であるgem/Railsエンジンへの分離やBundlerによる依存関係管理を用いた境界の遵守が、解決策の候補になるのではないかと思いました。

興味があれば元記事も読んでみてください。

*1:https://speakerdeck.com/kami/mountable-engine-for-small-team など

*2:『マイクロサービスアーキテクチャ』p.12(原文ママ)

このブログをHTTPS化した

はてなブログで独自ドメインを当てているときもHTTPS化できるようにとっくになっていたので、このブログもHTTPS化しました。

手順

こちらの記事を全面的に参考にさせてもらいました。

blog.jnito.com

まずはgemをインストールします。config.yml の準備もいい感じにやっておきます。

$ gem install hateblo_mixed_contents_finder

次に、mixed contentsが存在する記事を洗い出します。

$ hateblo_mixed_contents_finder validate_all
$ cat result.txt | cut -f 1 | sort -u > invalid_entries.txt # mixed contentsを含む記事のURLリストを出力する

ツールが invalid_entries.txt を参照してくれるので、再更新をかけます。

$ hateblo_mixed_contents_finder update_all

ふたたびmixed contentsがないかを確認します。

$ hateblo_mixed_contents_finder validate_all
$ cat result.txt | cut -f 1 | sort -u
http://blog.kymmt.com/entry/201601_read_booksq
http://blog.kymmt.com/entry/201602_read_books
http://blog.kymmt.com/entry/201603-read-books

ここまでやって、mixed contentsが残った記事のURLリストを出せたので、これらの記事については手作業でmixed contentsを取り除きました。具体的には、ベタ書きされたURLのプロトコルがHTTPだったので、次のように対応しました。

  • httphttps に置き換え
  • Amazonの画像URLは http://ecx.images-amazon.comhttps://images-na.ssl-images-amazon.com に置き換え

もう一度mixed contentsがないか確認たところ、そのような記事は見つかりませんでした。

$ hateblo_mixed_contents_finder validate_all
# ...
OK💚

最後に、はてなブログの設定画面からHTTPS配信を有効にしておしまいです。

『なるほどUnixプロセス』を読んだ

読みました。

tatsu-zine.com

どんな本か

主にRubyの Process モジュールを使いながらUnixプロセスについて知る本です。

Rubyでには Process モジュールを通じてUnixプロセスを操作することができます。つまり、プロセスに関するUnixのシステムコールをRubyから扱うことができます。この点で、Cから直接システムコールを発行するよりRubyプログラマにとっては読みやすいコードで、Unixプロセスについての解説を読むことができます。実際、Ruby製のWebアプリケーションでよく使われるResqueやUnicornも、Rubyでプロセスを操作しています。

原著の出版は2011年と少し古いですが、主題がそうそう古びるものではないので、出版年についてはほとんど問題にならないと思います。

具体的には次のようなことがらが説明されています。

  • プロセス自体の特徴
  • forkと子プロセスとの協調
  • シグナル
  • IPC(パイプ、Unixドメインソケット)
  • 外部コマンドを実行するプロセスの生成
  • 付録として実例の説明
    • Resque, Unicorn, Spyglass(この本オリジナルのpreforkサーバ)

なぜ読んだのか

最近はわりとアプリケーションレベルの知識に偏ってインプットしていたので、日常的にあまり意識しないOSレベルの知識についてインプットしたかったというのがあります。Rubyをふだん書いている身として、RubyでUnixのことについて説明しているところに興味を持ちました。また、ちょうど達人出版会でセール中だったからというのも理由のひとつです。

実際に読んでみると、1章1章がちょうどいい詳細度の説明になっていることと、自然な日本語に訳されていることから、たいへん読みやすかったです。

どうだったか

読んでみて興味深かった点をいくつか挙げます。

子プロセスの待機

Rubyでは fork(2) を呼び出す Kernel.#fork を使うと、子プロセスを作ることができます。そして、作った子プロセスが処理を終え、終了コードなどを返すのを待つには、waitpid(2) を呼び出すRubyの Process モジュールのAPIを使うことができます。しかも、子プロセスを待つシステムコールとしての waitpid(2) に対して、Ruby側のAPIとしては次のバリエーションがあるのがおもしろいところです。

このあたりは、ライブラリ利用時にできるだけ意図が明確になるようにする、という設計思想を感じます。

ゾンビプロセス

fork(2) を使って子プロセスを作ったあと、Unixカーネルは終了した子プロセスの情報をキューに入れて管理します。Rubyでは、この子プロセスの情報は Process.#wait またはそれに類するメソッドによって取り出すことができます。この情報が取り出されずに親プロセスが終了したり、親プロセスの処理がなかなか終わらないとき、キューに子プロセスの情報が残ったままになります。この場合、この子プロセスはゾンビプロセスと呼ばれます。これまでゾンビプロセスは「pstop を叩いたときに表示されるなにか」という意識しかなかったのですが、ここの説明で「なるほど」という気持ちになりました。

ゾンビプロセスはカーネルリソースの無駄使いとなるので、子プロセスを待たなくてよい場合、Rubyでは Process.#detach でスレッドを生成して、そちらのスレッドに待たせるのがよしとされているようです。

デーモンプロセス

デーモンといえば、バックグラウンドで立ち上がっているサーバの状態のことをそう呼んでいるという認識でした。たとえば、Rackサーバを次のように起動すると、デーモンプロセスとなり、起動後に制御が端末に戻ってきます。

$ rackup -D config.ru

プロセスをデーモンプロセスと呼ばれる端末から切り離されバックグラウンドで動くものにするには、次の手順を踏む必要があります。

  • forkしたあと、親プロセスで exit することで、子プロセスをわざと孤児にする
    • 親プロセスを終了させて、制御を端末に戻す
  • setsid(2) でその子プロセスをプロセスグループとセッショングループのリーダーにする
    • これをやらないと、端末などからSIGINTなどのシグナルが送られるとき、その子プロセスが終了してしまう
  • ふたたびforkして、親プロセスで exit することで、子プロセスをわざと孤児にする
    • この時点で、新たに作った子プロセスはセッションリーダーではなく、制御端末からも完全に切り離されている
  • カレントディレクトリが削除される場合の対策として、ルートディレクトリに移る
  • 標準ストリームをすべて /dev/null にする
    • 端末につながってないので持っていてもしかたない

また、Rubyでは Process.#daemon というメソッド一発でプロセスをデーモンにすることができます。そして、 Process.#daemon の中 ではCで上の手順をまさに実行しています。

デーモンプロセス化はもっとカーネルの機能一発で実現されているかのようなイメージを抱いていたので、このような細かい手順を踏んで実行することで成り立っているというのが、個人的には意外でおもしろかったです。

おわりに

プロセスというUnixにおいて動作するプログラムを抽象化した存在について知っておくことは、Rubyを書くこと以外の場面でも活きてくると思いますし、単純に世界が広がっておもしろいので、Rubyが読める人はこの本も読んでおくとよさそうだと思いました。サンプルコードで使われるライブラリはすべてRubyの組み込みライブラリか標準添付ライブラリの範疇に収まっているので、気軽に実行できます。実際に手を動かして読むと「なるほど」感が深まると思います。

また、preforkサーバの見本として付録についているSpyglassのコードを読んでみたところ、この本で出てきた知識をフル活用していて、かつ見本ということでシンプルなコードになっているので、やってることがわかるという感覚が得られたのがよかったです。コードはほとんどRubyで書かれています(HTTPパーサなど一部がC)。このサーバのコードをひととおり把握することで、Unicornなど本番で使われるWebサーバの内部の話題にもついていきやすくなりそうだと思いました。

原著者Storimer氏の続編として、TCPソケット編とRubyスレッド編があるようなので、日本語版はないものの、こちらにも手を出したいところです。

Books by Jesse Storimer | Short, focused technical books about system programming specifically for Ruby and Rails developers.