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

はじめての Go 言語のプロジェクトを AWS Lambda + API Gateway でやったのでパッケージ構成を晒すよ

はじめての Go 言語のプロジェクトを AWS Lambda + API Gateway でやったのでパッケージ構成を晒すよ

2019/01/31 開催のGo(Un)Confernce(Goあんこ)LT大会 5kg (https://gounconference.connpass.com/event/112942/) の発表資料です。

Shohei Okada

January 31, 2019
Tweet

More Decks by Shohei Okada

Other Decks in Programming

Transcript

  1. はじめての Go 言語 のプロジェクトを AWS Lambda + API Gateway でやったので

    パッケージ構成 を晒すよ Go(Un)Conference(Goあんこ)LT大会 5kg @okashoi
  2. • 構成: Go + AWS Lambda + Amazon API Gateway

    + DynamoDB • 一部に web クローリング処理を含む社内システム • 小規模(8 エンドポイント程度) • 社内初のフル Go 言語プロジェクト • Go 言語未経験者も多い 3 プロジェクト概要
  3. • 書籍『みんなのGo言語』の内容を倣った • レイヤードアーキテクチャを採用 • Semantic Import Versioning 6 全体像

    • import path にメジャーバージョンを含める • ほぼ確実に v2 が作られることはないが メンバーに「推奨されているやりかた」 を知ってもらう目的
  4. • controller, usecase, presenter • presenter は interface を定義して メディアタイプごとに実装

    (json, xml, text) 7 application 画像出典:Clean Coder Blog https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html
  5. • 他のパッケージに依存しない • import するのは標準パッケージに限定 • データ操作に関する interface を定義 •

    クローリングのための HTTP リクエストを CQRS における Query とみなした • 今思えば ~Repository じゃなくて ~Command という方が分かりやすかった? 8 domain
  6. • domain で定義した interface を実装 • DynamoDB への Read/Write •

    HTTP クライアント + HTML パーサ • 実際のクローリング対象ページの HTML を testdata 下に設置してパーサのテストを書いた 9 infrastructure
  7. • request/response を独自のものに変換してから~ • パッケージを AWS のものに依存させないため 11 main.go func

    newRequest(awsRequest events.APIGatewayProxyRequest) application.Request { return application.Request{ Path: awsRequest.Path, HTTPMethod: awsRequest.HTTPMethod, Headers: awsRequest.Headers, QueryStringParameters: awsRequest.QueryStringParameters, PathParameters: awsRequest.PathParameters, } } func convertToAwsResponse(res application.Response) events.APIGatewayProxyResponse { return events.APIGatewayProxyResponse{ StatusCode: res.StatusCode, Headers: res.Headers, Body: res.Body, } }
  8. • ルーティング • 愚直に文字列比較の switch case • これで充分な用途だったため 12 main.go

    switch { case req.HTTPMethod == "GET" && req.Path == "/v1/hoge": res = hogeController.Hoge(req) case req.HTTPMethod == "GET" && req.Path == "/v1/fuga": res = fugaController.Fuga(req) // ...(略) }
  9. • DI • 詳細→ • シンプルで Go らしくて素敵だと思った (初心者並感) 13

    main.go https://speakerdeck.com/morikuni/golang-dot-tokyo-number-11
  10. いまのところこんな感じで大きく破綻はしていない • これ以上大きくなるとやや自信ない(もうちょっと細分化したい) Go 未経験者がいるプロジェクトでもタスクが振りやすかった • まずは repository の 1

    メソッドからやってもらう • 習熟してきたら usecase 以下をまるごと任せる • 型 + ビルドが通っている事実 + テストコード = レビューするのも楽 14 やってみて所感
  11. JSON(HTTP レスポンス)←→ DynamoDB のマッピング方法悩む • 構造体定義にタグをつければ変換できるのは便利 • domain に定義したエンティティにタグをつけるとよさそうだが HTTP

    や DB に関する知識が domain に漏れ出すことになる • 最終的にはエンティティにつけることにしたが... • そういうことは考えずにシンプルにやろうというのが Go の思想っぽい? エラーハンドリングどうしようか悩む • 独自エラーを定義してログなどに欲しい情報を出力するなどした • 下位で生成したエラーを上位に渡していくのがやや煩雑 15 やってみて所感