play meetup-2-dev-best-practices
TRANSCRIPT
Play! framework を使用した 小規模プロジェクトにおける
Best Practices ()!
鹿島和郎(かしまかずお)
自己紹介• 鹿島和郎(かしまかずお)
• 家から出ないフリーのエンジニア。
• 同じく出不精のフリーエンジニア数人で 受託開発とかの仕事をしてます。
• リモート開発に最適化させた開発プロセスを作りたい と思って、いろいろやってます。
• GitHub: k4200, Twitter @k4200jp
話す内容
• Play! framework + その他、で何か作ってます。
• その小規模開発プロジェクトでの Best Practices を公開します。
• 懇親会の時に、みなさんの事例も聞かせて下さい。
こんなの作ってます
こんなの作ってます
以下のものをまとめて検索できます。
• GitHub issues • GitHub wiki • Slack • Facebook groups • Gmail • 添付ファイル(pdf, docx, xlsx etc.) • その他、順次対応中です。
小規模プロジェクトの開発環境
※全て個人的な意見です
開発環境3つの環境
• ローカル開発 → Vagrant
• テスト → VM x 1 on Azure
• 本番 → VM x 3 on Azure
開発環境ポイント
• Ansibleで一発で構築。
• Chef は小規模環境には向いていない気が。
• Puppet は使ったことない。
• Ansible Best Practices になるべく準拠。
• ディストリビューションは揃える。
開発環境CI は CircleCI を使用
• Play (sbt) も当然使える。
• 主要なDBやキャッシュサーバーが最初から起動している。
• 小規模プロジェクトなら無料プランで十分。
開発環境
Play! framework の話が出てこないんですけど・・・
開発環境テクノロジースタック(DB周り)
• Play! framework 2.3
• Slick 2.1 (ScalikeJDBCじゃなくてすみません)
• Flyway SBT plugin
• PostgreSQL 9.3
開発環境DB周りのポイント - テスト
• No inMemoryDatabase • あまりテストにならない。
• 違いを考慮したテストを書く必要があったり、 本末転倒。
• DBを使うテストは並列にしない。
• sbt でテストを動かす際の、3つのレベルの 並列性に注意。
開発環境DB周りのポイント - マイグレーション
• Evolutions ではなく Flyway を使う。
• Flyway はシンプルにSQLで書けてよい。
• evolutions の嫌なところ。
• ファイル名が <数字>.sql
• よく inconsistent state になる。
開発環境DB周りのポイント - マイグレーション
• Playアプリケーション起動時に勝手に起動する系は地雷。
• 設定でオフにしても、テスト時のFakeApplication で起動したりする。
• コマンドで手動実行、あるいはデプロイスクリプトに組み込む。
開発環境DB周りのポイント - Flyway
• ファイル名は数字ではなくてタイムスタンプにする。
• 番号がかぶるのを避ける。
• 例:V201507051534__Create_foo.sql
• outOfOrder を有効にする。
開発環境DB周りのポイント - Flyway
なぜ play-flyway ではなくてFlyway SBT plugin なのか?
• 後述のコード生成と相性が悪い。
• そもそも、起動するタイミングとかコントロールしたいし、プラグインを使う意味があまりない。
開発環境
DB周りのポイント - boilerplate
• Slick の code generator が便利。
• DB構造を読み込んでScalaのソースを作成してくれる。
• sbtプラグインがあるらしい。
開発環境
/** Entity class storing rows of table Conversation * @param id Database column id DBType(serial), AutoInc, PrimaryKey * @param dataSourceId Database column data_source_id DBType(int2) * @param lastUpdatedAt Database column last_updated_at DBType(timestamp) * @param status Database column status DBType(int2) */ case class ConversationRow(id: Int, dataSourceId: Short, lastUpdatedAt: DateTime, status: Short) /** GetResult implicit for fetching ConversationRow objects using plain SQL queries */ implicit def GetResultConversationRow(implicit e0: GR[Int], e1: GR[Short], e2: GR[DateTime]): GR[ConversationRow] = GR{ prs => import prs._ ConversationRow.tupled((<<[Int], <<[Short], <<[DateTime], <<[Short])) } /** Table description of table conversation. Objects of this class serve as prototypes for rows in queries. */ class Conversation(_tableTag: Tag) extends Table[ConversationRow](_tableTag, "conversation") { def * = (id, dataSourceId, lastUpdatedAt, status) <> (ConversationRow.tupled, ConversationRow.unapply) /** Maps whole row to an option. Useful for outer joins. */ def ? = (id.?, dataSourceId.?, lastUpdatedAt.?, status.?).shaped.<>({r=>import r._; _1.map(_=> ConversationRow.tupled((_1.get, _2.get, _3.get, _4.get)))}, (_:Any) => throw new Exception("Inserting into ? projection not supported.")) /** Database column id DBType(serial), AutoInc, PrimaryKey */ val id: Column[Int] = column[Int]("id", O.AutoInc, O.PrimaryKey) /** Database column data_source_id DBType(int2) */ val dataSourceId: Column[Short] = column[Short]("data_source_id") /** Database column last_updated_at DBType(timestamp) */ val lastUpdatedAt: Column[DateTime] = column[DateTime]("last_updated_at") /** Database column status DBType(int2) */ val status: Column[Short] = column[Short]("status") /** Index over (dataSourceId) (database name conversation_data_source_id_key) */ val index1 = index("conversation_data_source_id_key", dataSourceId) } /** Collection-like TableQuery object for table Conversation */ lazy val Conversation = new TableQuery(tag => new Conversation(tag))
1つのテーブルでこれくらい。
使ってて便利なライブラリ
便利なライブラリ
SecureSocial
OAuth を使っている各種SNS、webサービスへのログイン機能を簡単に作れる
Play用のライブラリ
便利なライブラリ
SecureSocial
• 2月から更新されてない。
• 作者はやる気はあるらしい。
• Play 2.4対応とか。
便利なライブラリSecureSocial
• その間、とりあえず fork したバージョンを使っている。
• Slack 対応などを独自で追加。
• 本家に PR は送っている。
• "tv.kazu" %% "securesocial" % "3.0.2"
便利なライブラリscalacache
• 複数のバックエンドを透過的に扱える。
• memoize が簡単で便利。import scalacache._ import memoization._ !implicit val scalaCache = ScalaCache(new MyCache()) !def getUser(id: Int): User = memoize { // Do DB lookup here... User(id, s"user${id}") }
便利なライブラリscalacache
• 質問とかにも、作者の Chris さん (日本語堪能)が親切に対応してくれる。
その他にも 色々話したいのですが、
続きは懇親会で。
ありがとうございました。