astamuse Lab

astamuse Labとは、アスタミューゼのエンジニアとデザイナーのブログです。アスタミューゼの事業・サービスを支えている知識と舞台裏の今を発信しています。

そうだScalaでAsta4dを使おう

こんにちは、yanagitaです。

いつもPlay関連の話をしていますが、今回はAsta4dのscala対応についてお話します。
すでに本ブログでもasta4dの関する話はでていますが、元々Java向けのライブラリとなっているためScalaで使用とすると Scalaの良さ活かせないところや仕様上使えないメソッドなんかがあったりします。
そのあたりを対応したAPIと回避策を紹介します。

過去のAsta4dに関する記事はコチラ

事前準備

Scala向けのAPIの利用するにはasta4d-scalaライブラリを追加する必要があります。
以下、maven向け

<dependency>
    <groupId>com.astamuse</groupId>
    <artifactId>asta4d-scala</artifactId>
    <version>0.6</version>
</dependency>

以下、sbt向け

libraryDependencies += "com.astamuse" % "asta4d-scala" % "0.6"

Renderer定義のScala対応

シンプル定義

asta4d-scalaライブラリを読み込むことでRendererの定義にArrowAssocが使用できるようになります。

以下、コーディングの比較
Java表記

public Renderer render() {
  Renderer render = Renderer.create();
  render.add("#someIdForInt", 12345);  
  render.add("#someIdForLong", 12345L);  
  render.add("#someIdForBool", true);  
  render.add("#someIdForStr", "a str");  
  render.add("#someIdForNull", (Object) null);  
  render.add("#someIdForClear", Clear);
  Element newChild = ElementUtil.parseAsSingle("<div></div>");  
  render.add("#someIdForElementSetter", new ChildReplacer(newChild));  
  render.add("#someIdForElement", ElementUtil.parseAsSingle("<div>eee</div>"));  
  render.add("#someIdForRenderer", Renderer.create("#value", "value"));  
  return renderer;
}

Scala表記

import com.astamuse.asta4d.scala.R._
def render(): Renderer = {
  "#someIdForInt" -> 12345 &
  "#someIdForLong" -> 12345L &
  "#someIdForBool" -> true &
  "#someIdForStr" -> "a str" &
  "#someIdForNull" -> (null.asInstanceOf[Object]) &
  "#someIdForClear"-> Clear &
  "#someIdForElementSetter" -> new ChildReplacer("<div></div>".parseAsSingle()) & 
  "#someIdForElement" -> "<div>eee</div>".parseAsSingle() &
  "#someIdForRenderer" -> ("#value" -> "value") &
  "#someIdForRenderable" -> (()=>("#id" -> "xx").asRenderer())
}

ArrowAssoc定義のお陰でシンプルで読みやすくなり、scalaで書く方にとっては単純にコーディングできるようになったかと思います。
少し解説します。
scala側でArrowAssocを使ったコーディングを行うために「com.astamuse.asta4d.scala.R._」をインポートします。
続けて、定義はTuple2の第一引数にセレクタ、第二引数に挿入する値を設定します。これが1つのRenderer定義になります。

// 結果は一緒
render.add(セレクタ, 挿入する値); // Java
セレクタ -> 挿入する値 // Scala

複数Rendererを定義する場合は「&」でTuple2を繋いでいきます。
これはインポートしたR配下のメソッドがArrowAssocで定義されたTuple2を暗黙的にRendererオブジェクトに変換し、「&」でRenderer#addメソッドに変換する仕組みとなっています。
暗黙部分を取り除くと下のイメージとなります。

Scala表記

  "#someIdForInt" -> 12345 &
  "#someIdForLong" -> 12345L &
  "#someIdForBool" -> true &

Java表記

  Renderer.create("#someIdForInt", 12345)
    .add(Renderer.create("#someIdForLong", 12345L))
      .add(Renderer.create("#someIdForBool", true))

では、次は上記以外のRenderer定義について紹介します。

配列定義

Java表記

public Renderer render() {
  return Renderer.create("#someIdForRenderer", Arrays.asList(123, 456, 789), new RowRenderer<Integer>() {  
    @Override  
    public Renderer convert(int rowIndex, Integer obj) {  
        return Renderer.create("#id", "id-" + obj).add("#otherId", "otherId-" + obj);  
    } 
  });
}

Scala表記

import com.astamuse.asta4d.scala.R._
def render() = {
  "#someIdForRenderer" -> List(123, 456, 789).map { i => {
    "#id"-> ("id-" + i) & "#otherId"-> ("otherId-" + i)
  }}
}

配列を使用した場合はJava側で定義していたRowRendererが不要となり、mapメソッドのみで記述できるようになります。

属性定義

Java表記

public Renderer render() {
  Renderer render = Renderer.create();
  render.add("#id", "+class", "yyy");
  render.add(("#id", "-class") -> "zzz");
  render.add("#id", "+class", "xxx");
  render.add("#id", "value", new Date(123456L));
  render.add("#id", "href", null);
  render.add("#idstr", "value", "hg");
  render.add("#idint", "value", 3);
  render.add("#idlong", "value", 3L);
  render.add("#idbool", "value", true); 
  return render
}

Scala表記

import com.astamuse.asta4d.scala.R._
def render() = { 
  ("#id", "+class") -> "yyy" &
  ("#id", "-class") -> "zzz" &
  "#id" -> "+class" -> "xxx" &
  "#id" -> "value" -> new Date(123456L) &
  "#id" -> "href" -> null & 
  "#idstr" -> "value" -> "hg" &
  "#idint" -> "value" -> 3 &
  "#idlong" -> "value" -> 3L &
  "#idbool" -> "value" -> true 
}

ArrowAssoc定義で属性を指定する場合は、セレクタ、属性指定、挿入する値を一括りで定義します。

  (セレクタ -> 属性指定) -> 挿入する値
  or
  セレクタ -> 属性指定 -> 挿入する値

いかがだったでしょうか?
asta4d-scalaでscalaライクなコーディングができるようになったと思います。

メソッド名と予約語のバッティング対応

URLルールの定義でパラメータを設定する役割としてvarメソッドが存在しますが、 この「var」scalaでは予約語となっておりメソッド名や変数名に使用することができません。 回避方法としては2つ、
 1つ目がメソッド名をアクサングラーブでかこ囲って使用する。
 2つ目がvarメソッドと同じ処理を持つpathVarメソッドを使用する。
これでメソッド名のバッティングを回避できます。

// ↓ コンパイルエラー varが予約後
rules.add("/app/{name}/{age}", "/templates/variableinjection.html")  
     .var("extraVar", 1234);
// ↓ アクサン グラーブで囲ってコンパイルエラーを回避
rules.add("/app/{name}/{age}", "/templates/variableinjection.html")  
    .`var`("extraVar", 1234); 
// ↓ pathVarメソッドでコンパイルエラーを回避
rules.add("/app/{name}/{age}", "/templates/variableinjection.html")  
     .pathVar("extraVar", 1234);

個人的には後者を使って頂くのが違和感がない実装になるかと思います。

以上、JavaライブラリAsta4dをscalaで使用するための紹介でした。
今後Scala対応が進んでいく中で興味を持って頂ける方が少しでも増えることを願っています。
更新があればまたこちらでご紹介します。

■ asta4d-scala github.com

弊社まだまだエンジニア&デザイナーを絶賛募集中です。

Webデザイナー
Webアプリケーションエンジニア(Java)
Webアプリケーションエンジニア(Scala)
データ開発エンジニア

採用に関するQAコチラ lab.astamuse.co.jp

アスタミューゼの開発組織と採用に関するQAアレコレ

こんにちは。並河(@namikawa)です。

随分と冬の足音が近づいて来た今日この頃、すっかりラーメンがより美味しく感じる季節となってまいりましたが、皆様いかがお過ごしでしょうか。

さて、今日はいつもとは趣向を変えて、開発組織の話をしようかと思います。

弊社では、優秀なエンジニアやデザイナーを絶賛大募集しているのですが、今年くらいから、私自身が本格的にエンジニア採用に携わるようになりました。このエントリでは、弊社での採用・選考(面接、面談)をどうやっているかと、その場でよく質問されることに対するQA集をまとめてみようかと思います。

弊社での業務に少しでも興味をお持ちの方に、弊社がどのような開発組織で、エンジニアがどのような働き方をしているのかのイメージ形成に役立てていただければと思います。

ちなみに募集職種や要項などは、以下の採用サイトに記載しておりますので、その補足エントリ、くらいで見てもらえれば幸いです。

弊社のエンジニア面接・面談について

f:id:astamuse:20171115181155j:plain

Q. 面接回数と時間はどのくらいですか?

大体のケースでは2〜3回で、1回あたり30〜60分程度です。面接時間が長くなる場合は、QA時間が長くなるケースが多数かなと思います。

お互いの事情で面接前に相談した上で、面接自体の回数(面接担当の組み合わせ等で調整)も変動することがあります。

Q. 面接はスーツ着用ですか?

服装はスーツでなくても大丈夫です。むしろ、普段着ているような格好で来ていただければと思います。

尚、弊社の業務中の服装は、TPOさえ考慮いただければ基本的に自由ですので。(夏は、Tシャツ・短パン・サンダル、の方もいらっしゃいます。)

Q. 面接でどういうところを見ているのですか?

面接の担当者によって様々かとは思います。

個人的には、できるだけ普段の雰囲気でかつ雑談のような感じで、カジュアルにお話しできればなと考えながらやらせてもらっていますが、端的にいうと、これはお互い様かとは思いますが、目の前に座っている方と楽しく一緒に仕事ができそうかどうか、ですかね。

サービス開発について

Q. 開発環境ってどんな感じですか?

  • 開発言語については、 Scala、Java、Python などを採用しています。
  • データベースや検索関連は、PostgreSQL、MongoDB、ElasticSearch、Groongaなどを利用しています。
  • データの生成や分析に Hadoop や Spark などの大規模データ処理基盤を構築・活用しています。
  • サービスインフラについては、Google Cloud Platform 等のクラウドサービスを中心に整備を進めています。

色々書きましたが、現状よりもより良いもの(技術・ツールなど)があれば常時、積極的に検討・導入しています。基本的に技術の選択については、各エンジニアが裁量を持ち責任とセットで導入してもらっている感じです。

また、支給されるPCについては、自身の開発環境を自由に選べるようにしています。また、拡張・増設など(モニタ、メモリ、SSD、キーボード等)も自身の判断で申請できます。

あ、そうそうオフィスの椅子は、ハーマンミラー社のエンボディチェアです。でも、個人的には会議室にあるアーロンチェアの方が好みなので、取り替えようかなぁと思っています。

正直、一番生産性が上がる形であれば、できる限りのサポートはしたいと思っています。人によってはスタンディングで作業していたり、バランスボールに乗っていたりと様々ではありますが・・・。

Q. エンジニアの方の1日の過ごし方を教えてください

基本的には、自社サービスでの開発となりますので、マーケティング担当やディレクターと一緒にサービスのKPIに沿った施策を検討し、そのあとは実際にプログラミングとテストを行いリリースとなります。

1日といった区切りで言うと、少々の打ち合わせ以外は、ほぼ何かしらのモノ作りに取り組んでいるイメージです。

f:id:astamuse:20171115171605p:plain http://recruit.astamuse.co.jp/

例えば、上記の画像は、弊社の採用サイトのトップページですが、実際にアクセスしてもらうと、ある日の1日のスナップショットをコマ送りにした様子が確認できます。(PCからのみです。すみません。)

これを見ると、社内の1日の雰囲気を確認できるかなと思うのですが、多数の方が基本的には自席でコーディングしていることがほとんどです。

あと、採用サイトにも社員の1日の例が記載されているので、そちらもご覧ください。

働き方について

Q. 全体で何人くらいで、どういうチーム構成ですか?

2017/11/15現在、会社全体では約50名、開発・デザインの組織は、エンジニア10名、デザイナー4名となります(全て正社員)。サービス・データ開発のほぼ全てを内製化し、開発を進めています。

エンジニアは、アプリチーム・データチーム・インフラチームの3チームにわかれてサービスやデータ開発に取り組んでいます。各チームや職種の詳細については、(採用サイト)をご覧くださいませ。

Q. 勤務時間や残業時間はどんな感じですか?

開発・デザイン本部はフレックス制を導入していて、コアタイムは少し長めではありますが 10:30~17:30 となっています。基本的には、8時間 x 営業日数 (有給使用日数は除く) を一月単位で満たしていただければ良いという仕組みです。

それぞれが課されたタスクによって、自分にあった仕事の進め方で取り組んでいるため、みんな出社・帰社時間はバラバラです。だからこそみんな気にせず定時に帰れる雰囲気があり、残業時間は月平均で10時間未満です。

あと、巷で話題になりがちなリモートワークをしているエンジニアもいます。

Q. 研修制度とかってありますか?

明示されている(ルール化されている)ものはありませんが、良質なインプットが良質なアウトプットを産みだす、という考え方ですので、外部セミナー・勉強会への参加や登壇を奨励しています。

デブサミや Google Cloud Next や db tech showcase などは、毎年社員の多数が参加していますし、昨年の Cloudera World Tokyo では弊社のメンバーが登壇しています。

こういったカンファレンスやセミナーで情報収集することも大事なのですが、どちらかというと、こういった場で多くの刺激を受けて、業務につなげてほしいという期待を持っているので、基本的には参加にストップを出すケースは、よほどの事情がない限りほとんどないかと思います。

尚、必要な書籍は会社経費で購入できる他、持ち回りで毎週社内勉強会を開催していたり、このエンジニアブログの執筆をしています。

その他

Q. 会社の雰囲気ってどんな感じですか?

文章で表現するのが難しいのですが、割と落ち着いている方だと思います。

開発・デザイン本部ですと既婚者も多く、ご家庭の事情で早めに来て、早めに帰る方も多いですし、業後に勉強されている方もいます。

あとは、最近だと隔月くらいで会社全体の懇親会(任意参加)を開催していて、オフィスで他事業部の方とお酒を交えながらカジュアルな感じで話をする機会もあります。(大事)

懇親会の後に有志でボードゲームをやったり、個人的にはラーメン部(非公式)を作って活動日を決めていて、銀座・築地・新橋界隈の美味しいラーメン屋にメンバーとランチに繰り出しています。

f:id:astamuse:20171115181159j:plain

↑オフィスでの懇親会の雰囲気

Q. 社長ってどんな人ですか?

こんな人(以下リンク先)です。最近太っ・・・いや、なんでもないです。

Q. エンジニアってどういう人がいますか?

最近、エンジニア3名がそれぞれインタビューを受けましたので、僭越ながらそちらを紹介しておきます。よろしくお願い致します。

https://www.astamuse.co.jp/information/2017/interview_namikawa/

https://www.astamuse.co.jp/information/2017/interview_yamagata/

https://www.astamuse.co.jp/information/2017/interview_fukuda/

最後に本題

と、駆け足でしたが、弊社の開発・デザイン組織の雰囲気が少しでも伝われば幸いです。

冒頭でも書きましたが、弊社ではエンジニア・デザイナーを絶賛大募集しておりますので、少しでも気になれば、カジュアルにランチでもしながらお話ししましょう。またはお手数ですが (@namikawa) までDM等いただければと思います。

それでは!=͟͟͞͞(๑•̀=͟͟͞͞(๑•̀д•́=͟͟͞͞(๑•̀д•́๑)=͟͟͞͞(๑•̀д•́

コード管理と構成管理(その1〜 バッチデプロイしちゃうよ?編 〜)

こんばんにちは。今週ブログを担当するnishikawaです。早いものでアスタミューゼに入社してからもう10ヶ月が経過しました。本当に時間が経つのは早いなぁと驚いております。

さて、今回はタイトルにも書きましたが弊社のサービスの一つである転職ナビで利用しているバッチをデプロイしたときに感じたコードの管理と構成の管理の話を数回に分けてしたいと思います。

転職ナビは求人票を掲載するためのアプリケーションと、データを色んなアプリケーション間で授受したり、メールを送信するなど裏で色んな作業をしてくれるためのバッチ群に大きく分かれます。

で、後者は大小さまざまなものが存在し、ひしめき合いながら日々の作業を行なってくれているのです。

そんなバッチさん、大体はScalaで作成されているものが多く本番の環境にはデプロイ時に生成したjarファイルが配置されているのですが、それをさらにbashスクリプトでラップするというよくあるパターンで構成されています。

これが本番環境およびステージング環境に配置されているわけですが、このプロダクトの管理方法、いくつか問題があるなぁと色々開発していて感じました。それは・・・

  • アプリケーションの設定ファイルが配置する環境ごとに違うものを作成しなければいけない(というか作成されているw)
  • スクリプトも複数環境分用意しなければいけない(というかされているww)
  • 挙句jenkinsなどのCIツールでプロダクトのデプロイを考えたときにデプロイスクリプトも複数作って用意しなければならない(というかされているwww)

とういことになり管理が煩雑になりがちです。 この状態が続くと設定ファイルの反映漏れや配置ミスが発生しデプロイしたけど動かないという問題や一応動いているけど本番環境とステージング環境で差異が出るなど運用面でよくないことが起こります。

そこで、これらの問題を解決するために実際にやってみたことや、それに対する考察していきたいと思います。

やったこと: 構成管理用のフレームワークを使用してみる

まず実際に私が半年ぐらいでやってみた構成を書いてみます。

バッチさんの紹介

お名前 求人票データの取り込みバッチ
おところ 転職ナビ保有の本番バッチサーバおよびステージングサーバ
種別 Scala製
特技 特定ディレクトリに配置された求人票(CSVファイル)をデータベースに投入するよ!

構成(Before)

今回の「求人票データの取り込みバッチ」の開発プロジェクトのディレクトリ構成は以下です。

joboffer-csv-import-batch
├── csvroot
│   ├── astamuse
│   │   └── csv
│   └── <提携先ごとの求人票配置ディレクトリ>
├── joboffer-csv-import-batch.iml
├── pom.xml
├── project
├── shell
│   ├── local
│   │   ├── CsvImportForAstamuse.sh
│   │   └── <各提携先の実行スクリプト(ローカル用)>
│   ├── production
│   │   ├── CsvImportForAstamuse.sh
│   │   └── <各提携先の実行スクリプト(本番用)>
│   └── staging
│       ├── CsvImportForAstamuse.sh
│       └── <各提携先の実行スクリプト(ステージング用)>
├── src
│   ├── main
│   │   ├── resources
│   │   │   ├── application.conf
│   │   │   ├── application_production.conf
│   │   │   ├── application_staging.conf
│   │   │   ├── logback-production.xml
│   │   │   ├── logback-staging.xml
│   │   │   └── logback.xml
│   │   └── scala
│   │       └── <ソースコード群>
│   └── test
│       └── <テストコード群>
└── target

以上を見て分かる通り、スクリプトも設定ファイルも本番用、ステージング用、ローカル試験用で別れているのが分かります。 そして、これらが配置後は以下のようになります。

joboffer-csv-import-batch
├── CsvImportForAstamuse.sh
├── <提携先ごとのスクリプト>
├── csvroot
│   ├── astamuse
│   │   ├── csv
│   │   └── old_csv
│   └── <提携先ごとの求人票配置ディレクトリ>
├── joboffer-csv-import-batch.jar
└── logs

で、この構成を実現するために以下の手順でデプロイを行います。

■ローカルで実施

  • ビルド用のワークディレクトリを作成する
  • gitリポジトリから資材をダウンロードしてくる
  • 設定ファイルをコピーする
  • ビルドする

■リモートで実施

  • デプロイ先のディレクトリを全て作成する
  • スクリプトをコピーする
  • ビルドされたjarファイルをコピーする

さて、上のデプロイ手順、一つずつ手でやっていたらとても面倒です。 弊社ではjenkinsを利用してこれらの作業をやっているのですが、使用しているのはpythonスクリプトです。 そのため、jenkinsで利用するスクリプトも本番環境用とステージング環境用で分かれております。

構成(After)

そこで、構成管理用フレームワークを利用し先に述べたデプロイの手順を実施します。以下は私が作った定義ファイル群とそのディレクトリ構成です。(chefが使えないのでAnsibleです。Chefファンの皆様ごめんなさい。

joboffer-csv-import-batch/
├── host_vars
│   ├── hosts
│   ├── localhost.yml
│   ├── prd-tennavi-bch01.yml
│   └── stg-tennavi-bch01.yml
├── roles
│   ├── create_package
│   │   ├── files
│   │   │   └── conf
│   │   │       ├── application.conf
│   │   │       └── logback.xml
│   │   ├── tasks
│   │   │   └── main.yml
│   ├── deploy_package
│   │   ├── files
│   │   │   └── shells
│   │   │       ├── CsvImportForAstamuse.sh
│   │   │       └── <提携先ごとのスクリプト群>
│   │   └── tasks
│   │       └── main.yml
│   └── get_repository
│       └── tasks
│           └── main.yml
├── site_create_package.yml
├── site_local.yml
├── site_prd.yml
└── site_stg.yml

これを利用するためにとった開発プロジェクトの構成が以下になります。

joboffer-csv-import-batch
├── joboffer-csv-import-batch.iml
├── pom.xml
├── project
├── src
│   ├── main
│   │   ├── resources
│   │   │   ├── application.conf
│   │   │   └── logback.xml
│   │   └── scala
│   │       └── <ソースコード群>
│   └── test
│       └── <テストコード群>
└── target

結果

いかがでしょうか?テコ入れをする前の構成に比べてディレクトリがすっきりしていることと思います。 ここで注目していただきたいのは本番やステージングにアップロードするための設定が環境ごとに存在しないということです。 これで設定漏れが最小限に防げると思います。 当たり前といえば当たり前の構成ですが、構成管理用のフレームワークがなかった頃は、この構成にするためには手順書をたくさん書かないといけなかったので、それがコードだけで管理できるというのがどれほど素晴らしいか理解していただけるかと思います。

以上で、ある程度の煩雑さは解消されました。しかしまだ この構成でも煩雑さは残ります。 これについては次回以降、考察を行っていきたいと思います。それではまた今度。

Copyright © astamuse company, ltd. all rights reserved.