ぜったいってなに

Software Engineerのブログです。

Play Scala周りで出てくるよくわからん単語を整理していく

Scalaを触ってると聞いたことない単語がバンバン出てくる。
参考となるドキュメントを漁ってみても、Java上がりの人向けに書いてる記事が多く、
業務でJavaを使ったことがない人間からしたら結構ツラい。


それでもPlay ScalaでDBアクセスをしようとがんばって調べつつ設定をしていたが、
だんだんわけがわからない状態になってきたので、一旦言葉の意味と関係性を整理したい。


DBアクセス用ライブラリ

調べてみたらこちらの記事が欲しい情報をほぼ全て網羅していた。

takezoe.hatenablog.com

一応メモしたことを書いておく。

JDBC

JavaRDBとの接続用API
conn = DriverManager.getConnection()して
conn.createStatement()したStatementオブジェクトにSQL文を渡して
executeQuery()ってしたら漸くDBにCRUDができる。
Webアプリケーション開発で初めて触ったのがRailsActiveRecordだったので
かなり面倒に感じる。

Hibernate

JavaのORMライブラリ。
JDBCを使ってRDBMSと接続しているっぽい。
実装用のインタフェースが直接JDBCを扱うより充実しているんでしょう。
使ったことはないのでわかりませんが。

ScalikeJDBC

Scala用のORMライブラリ。
これもJDBC上で動くっぽい。


RDB

H2

組み込み用RDB。DBサーバとしても使える。
Android上に載っているSQliteと同じカテゴリ。
JDBC, Hibernateなどを使ってアクセスできる。

MySQL

PostgreSQL

ry

MongoDB

r


カテゴライズできないもの

Akka

Scalaのライブラリ。並行処理やアクターモデルに対応している。
ここが詳しい。

www.slideshare.net

Maven

ソフトウェアプロジェクト管理ツール。
ここが詳しい。

1. Maven とは | TECHSCORE(テックスコア)


Play Scalaにどう関連するかというと、
Maven RepogitoryにあるlibraryDependencies
build.sbtに追記すると、ビルド時に記入したソフトウェアをインストールしてきてくれる。


例えばDB周りでslickpostgresqlを使いたい場合は以下のように書く。


build.sbt

1 name := """play_test"""
2
3 version := "1.0-SNAPSHOT"
4
5 lazy val root = (project in file(".")).enablePlugins(PlayScala)
6
7 scalaVersion := "2.11.7"
8
9 libraryDependencies ++= Seq(
10   cache,
11   ws,
12   "org.postgresql" % "postgresql" % "42.1.4",
13   "com.typesafe.play" %% "play-slick" % "2.0.0",
14 )

ここまででわかったこと

言葉の関係性を整理した結果、 DB周りに関してはこんな感じの組み合わせができる、ように見える。

  1. Play Scala * slick * PostgreSQL
  2. Play Scala * scalikeJDBC * MySQL
  3. Play Scala * anorm * H2

など。


今回のWebアプリケーションでは1の組み合わせを採用したい。
PostgreSQLを含めたNoSQLをまともに触ったことがないので、この機会に触ってみたい。

テーブルカテゴライズ

業務でテーブル設計をレビューしていただいて、色々考えることがあったので、例え話で書きます。


繰り返しますが全て例え話です。



前提

  • Appleの製品をRDBMS上でテーブルとして扱いたい。
  • 扱いたい製品はiPhoneiPadの2種類で、それぞれシリーズによって分類したい。
  • 製品はロジック上は違いを意識したくない。
  • 製品情報はシステム上では末端の情報で、他のロジックに対して及ぼす影響は少ない。

このようなシステム要件に対して、以下のような設計をして、案1として提案した。


apple_productsテーブル

column_name type description
id int なし
purchase_date date 購入日
product_type int 1: iPhone, 2: iPad


iphonesテーブル

column_name type description
id int なし
apple_produt_id int apple_productのid
product_type int 1固定
series int 1: iPhone6, 2: iPhone6S, 3: iPhone7
serial_number text シリアルナンバー
phone_number text 電話番号


ipadsテーブル

column_name type description
id int なし
product_type int 2固定
apple_produt_id int apple_productのid
series int 1: iPad, 2: iPad mini, 3: iPad Pro
serial_number text シリアルナンバー


apple_productsの1つ1つのレコードにはiphonesのレコードかipadsのレコード、
どちらか1つがくっついている、という構成。

f:id:zettaittenani:20170909162523p:plain


Rails上の実装はこんな感じをイメージしていた。

class AppleProduct < ActiveRecord::Base
  has_one :iphones
  has_one :ipads

  def product
    return iphone if product_type == 1
    ipads if product_type == 2
  end
end



この設計がどうか、という相談をしたら、
以下のようにしたらいいのでは、というお話をいただいた。 これを案2とする。


apple_productsテーブル

column_name type description
id int なし
purchase_date date 購入日


specific_productsテーブル

column_name type description
id int なし
apple_produt_id int apple_productのid
product_type int 1: iPhone, 2: iPad
series int 1: iPhone6, 2: iPhone6S, 3: iPhone7, 4: iPad, 5: iPad mini, 6: iPad Pro
serial_number text シリアルナンバー
phone_number text 電話番号 ない場合は空文字


つまり、「iPhoneiPadもいっしょくたにして1つのテーブルにしてしまえば?」というお話。


f:id:zettaittenani:20170909163256p:plain


この例ではiphonesテーブルとipadsテーブルのカラムの違いはphone_numberだけだが、 実際に業務で使っているテーブルはカラムの違いが他にもあって、 今回の例に照らすと、specific_productsipadの情報を入れたら、 そこそこの数のnullなり空文字なりが入って歯抜け情報になってしまうという状況。


それでもこっちの構成のほうが楽じゃない?と言われて、理由が全然わからなかった。 歯抜けの情報があるって気持ち悪いし、別のものなら別のものとしてテーブルを分けるのが自然な発想なんじゃないの?って。


で、理由を訊いてみたら以下のような内容だった。

  • apple_productsのレコードが、自分の持っているproductの情報を知らなくてすむ。
    案1だと、自分の持っているproductがiphoneなのかipadなのかをapple_productが知っていることになる。それはapple_productにとってはどうでもいいこと。
  • RDBMSの観点から明らかに案2の方がシンプル。Rails上でもAppleProduct.includes(:specific_product)と書ける。
    案1では、DBに問い合わせして結果が返ってくるまで各apple_productsレコードがiphone, ipadどちらを持っているのかわからないので、無意識にjoinしたりincludeしたりできない。
  • 確かにnullや空文字など歯抜けの情報はあるが、それはRails上でうまいことやればよい。


これらの理由は納得できるものだったので、最終的に案2の方針でいくことに決めた。


持ち帰って改めて考えてみたけど、今まで自分の中に「設計はできるだけ実体に即したものにする、もしくはがんばってそれに近づける」という発想しかなくて、 「データとして楽に扱えるようにする」という発想がなかったと思った。 「システムとしてはこういうものができていればいいね」という段階までビジネス要件が落とし込んであれば、 設計はエンジニアが構成を見てどういうものなのかがわかれば良くて、あとはデータとして楽に扱える、ということを主眼において考えればよいのかなと思う。


案2のRails上の実装も考えてみたけど、こんなかんじになるだろうか。


apple_product.rb

class AppleProduct  < ActiveRecord::Base
  has_one :specific_product

  def product
    specific_product.product
  end
end


specific_product.rb

class SpecificProduct  < ActiveRecord::Base
  belongs_to :apple_product

  def product
    return IPhone.new if product_type == 1
    IPad.new if product_type == 2
  end

  def iphone
    IPhone.new
  end

  def ipad
    IPad.new
  end
  
  class IPhone
    attr_reader :product_type, :type, :searial_number, :phone_number

    def initialize
      @product_type = product_type
      @series = series
      @searial_number = searial_number
      @phone_number = phone_number
    end
  end

  class IPad
    attr_reader :product_type, :type, :searial_number, :phone_number
    def initialize
      @product_type = product_type
      @series = series
      @searial_number = searial_number
      @phone_number = "-"
    end
  end
  private_constant :IPhone, :IPad
end


別の話になるけど、色々考えてるうちに、ORMとしてのActiveRecordって何を隠蔽してるんだ?と思った。
Rails上でレコードをオブジェクトとして扱えるっていうのは便利だと思うけど includesとかするなら結局テーブル構成見なきゃいけないし、SQL文がちょっと楽に書けるようになっているだけでは…?

Play Scala 触っててわかったこと

標題の通り。



activator runでサーバ立ち上げた後、下記は次のアクセス時にオートロードされる。

  • ファイルの中身変更
  • ファイル名変更
  • ファイル新規作成


オートロードされるというか、次のリクエストが飛んできたら
project内にファイルないしコードの差分があるかどうかを検知して
コンパイルし直す、みたいなことをしているっぽい。
編集したコードのインスタンスを呼び出す画面とは違う画面にアクセスしても、
編集部分のエラーがコンソールログに吐かれる & 画面に表示される。



詳しくはまだわからんけど、
「システムのどっかに1こでもエラーがあったら何も表示したらんぞ」
ていうスタイル、に見える。
つまりエラーが出ない状態になった時点でシステムに型エラーがない(少ない?)ことが
ある程度担保されているわけで。
これは好ましい仕組みかなと思う。


なんかエラーとかバグとか、言葉の意味を自分の中で混同している気がする。
RailsもSyntax Errorくらいなら出してくれるし、
Playである、というかScalaであることの恩恵に関してもっと理解が必要だなと思った。

レアジョブに申し込んでみようとした

「エンジニアに最も必要な言語は英語」ってどっかで読みました。

WritingやReadingはある程度自分ひとりでできますが、会話は相手がいないとできないですね。



英語が普通に喋れるようになりたい。仕事で使えるようになりたい。
でも時間はないし金はかけたくない。



てかんじで、オンライン英会話に行き着きました。


調べたら日本のオンライン英会話サービスはDMM英会話とレアジョブの二大巨頭って感じ。
ここらへんの記事を読んでみる。

englishlands.net



みたとこ、専門性が高くてビジネスに使う分にはレアジョブがいいんじゃない?と読めた。
ので、レアジョブを使うことにする。


で、安く申し込みしたいなーと思ってたらこんな記事も見つけた。

restartlog.com



毎月10%オフってめっちゃ安いやん。



てことでベネフィット・ワンに登録して申し込んでみる。



クーポン紹介記事には10%オフクーポンが載っていたが
2017/10/31までに申し込むと更に6レッスン分の料金が無料らしい。

f:id:zettaittenani:20170907195523p:plain



すご。



ただ1つ引っかかったことがあって。
これ、月割で支払うのか一括で支払うのかどっちなんやろ。



レアジョブに電話して訊いてみましたが、
ベネフィット・ワンからの申込みだと一括支払いとのこと。


日常英会話コース 毎日25分・6か月プラン 37,584円 ⇒ 33,800円
ビジネス英会話コース 毎日25分・5か月プラン 52,920円 ⇒ 47,600円



…一括か。


ってことで一旦保留にしました。
多分申し込むと思うけど。



ちなみにレアジョブ上でクレカで申し込んだら月ごとの支払いとのことです。
参考までに。

Play Scala Controller, Viewの追加

やること

  • Controllerを定義する。
  • Viewを定義してブラウザ上で表示させる。

設計はこちらを参考に。




やったこと

/app/controllers下にArticleController.scalaを作成する。
書き方は既存のサンプルのControllerに倣う。

package controllers

import javax.inject._
import play.api._
import play.api.mvc._

@Singleton
class ArticleController @Inject() extends Controller {
  def index = Action {
     Ok(views.html.article.index("Articleだぞ"))
   }
 }



次に、/app/views/下にarticleディレクトリを作成して、
その中にindex.scala.htmlを定義する。

@(message: String)
<h1>Articleです</h1>
<p>ぜったいってなに</p>


詳しくはわかってないけど、現状viewに渡したmessageは呼び出さないと
「controllerで引数渡してるのに参照してねえぞ」みたいなエラーが出るので、
しょうがなく@(message: String)を書いておく。



次。
/conf/routesでroutesを設定する。 書き方は既存に倣う。

GET     /article                    controllers.ArticleController.index



画面を作るのに必要そうなことはやった。

projectのhomeディレクトリでactivator runして
localhost:9000/articleにアクセスする。

f:id:zettaittenani:20170906224428p:plain

ださ。
90年代のホームページってかんじ。

でもまあとりあえずできた。



  • TODO
    • DB接続 (postgresql)
    • model定義
    • seedsでレコードサンプルを定義して適当に一覧表示する

転職ドラフト

登録しました。

job-draft.jp

明日ドラフトだって聞いたのでレジュメ頑張って書いたんですけど
審査通らなくて、修正して再申請したけど駄目だったんで今回は諦めます。

これで自分のおおよその市場価値をはかろうと思ったんですがそもそも土俵にすら立ててないですね。
「技術に関するアピールをもっと書いてください」ていうフィードバックをいただいたんですが、
技術に関して「俺こんなんやったぜ!」みたいに言えること特にないなーと思って。
まずいな。

Play ScalaでWebアプリケーションを作る

標題の通り。

ブログっぽいものを作る予定。

「Play ScalaRailsみたく触れるようにする」が目的。
ゆくゆくはGraphQLでAPI実装とかやる。

アプリのテーブル設計はこんな感じ。
超シンプル。(sqldesignerで書きました) f:id:zettaittenani:20170904070457p:plain

user: ログインユーザ
article: 記事
comment: 記事に対するコメント

記事はひとまず画像とか無しで、文字のみ。

画面設計は以下をイメージしている。

画面 Controller action
Home Home index
ログイン画面 User login
記事一覧 Article index
記事詳細 Article show
記事新規作成 Article new
記事編集 Article edit


記事一覧に「記事を新規作成する」ボタンがあって
記事詳細に「記事を削除する」ボタンがある。
記事詳細ではcommentも投稿できて、
投稿したらComment#createしてArticle#showに戻る、かな。

Rails書いてていつも思うけど、User#loginてリソースルーティングに乗ってないけど
無理矢理にでも、たとえばUser#newlogin機能を割り当てたりしたほうがいいのだろうか。

「名前は実体に即するように」 VS 「Railに乗る」

で、気になって調べたけど、Railsではこんなかんじに書いてある。

んー基本的には
「resourceに対する操作でリソースルーティングに含まれるactionに
適切なものがあるならばそれを選んで、ないならば別でactionを定義する」が正しいか。

じゃあ今回はUserindexしないしshowしないしnewしないしcreateしないし、、、で
User#loginが正しいことになるか。

何かご意見ありましたらください。

まま、とりあえず設計はおいといて手を動かすということで、
IntelliJ IDEA上のSBT Shell上でrunして localhost:9000で動かしてみました。

f:id:zettaittenani:20170904231718p:plain

Initしたぞ。

以上。