Scala関西Summit2018に参加してきました。忘れないうちにセッションの感想などをメモとして残しておきます。
初の2days開催とのことで、初日は以下のセッションを見ました。あ、僕も登壇者だったので最後のセッションは僕の自身の感想など含めてます。
- http4sとcats-effectで可愛らしい、関数型らしいアプリケーションを書こう!٩(๑^o^๑)۶
- circeから学ぶ Generic Programming 入門
- ZOZOSUITの裏側はScalaで動いてるよ!
- セッションを見ずにブース担当
- Akkaを分散トレーシングで見てみよう
- PHPエンジニアによるScalaエンジニアへの転身とその手引き
- Scalaでつくる証券会社とスタートアップ
- 自分の登壇枠
セッションの感想
http4sとcats-effectで可愛らしい、関数型らしいアプリケーションを書こう!٩(๑^o^๑)۶
スライド資料のリンク: https://speakerdeck.com/dysangelist/tagless-final
サンプルコードのリンク: https://github.com/dysangelist/scala_kansai_demo
感想
タラソワダーシャさんによる魂ほとばしる発表。可愛いの概念がすごく伝わりました!
主要なキーワードとしては以下。
- Scalaは可愛い
- cats-effect推し
- Futureは可愛くない
- http4sは可愛い
- Tagless Finalは可愛い
cats-effect
はScala用のIOモナドを提供するライブラリの一つですね。僕は使ったことないですが、Monixがこのライブラリに依存しているようですね。あまり気にしてませんでした。指数関数的なバックオフが簡単に実装できるようですね(Example: Retrying with Exponential Backoff)。 まぁ、僕はActor以外の箇所ではmonix.eval.Task
を使ってバックオフの実装を書いたりします(実装例)が、IOでも簡単そうですね。
「Futureが可愛くない」とか、「参照透過性を崩す」とか書かれていますが、Future上で実行する副作用を持つタスク側が問題では?と思いました。参照透過を崩す云々というより、Future#apply
を呼ぶと先にスケジューリングされてしまうことが、可愛くないと個人的には思います。(そうmonix.eval.Task
なら可愛いかもしれないです)
http4sは使ったことないですが、Finagle/Finatraに近いイメージを持ちました。が、よりFunctionalなスタイルで記述できるのがよさそうですね。Tagless Finalは僕も使ってます。型クラスに使い慣れてくれば、自然に使いたくなるテクニックだと思いました!
ソースコードで気になるところ
図書館のドメインを例にしたサンプルコードが紹介されていました。重要そうなユースケースは、「本を借りる」、「本を予約する」、「本を返却する」のようですね。
残念ながら、modelパッケージにあるケースクラスには、振る舞いがありません。これらのドメインオブジェクトは、いわゆるドメインモデル貧血症です。データを保持しているだけのクラス(データクラスという)ですね。
先ほどの重要なユースケースは、Logicクラスに実装されています。F[_]: Monad
にラップされたオブジェクトを返すので、for式で演算を合成できるのはよいとしても、Bookを理解するにはBookだけをみてもわからないというのが問題です。これはドメインによって駆動していない状況です。Bookにまつわる知識を、Book以外に配置せずにBookに集中させるべきでは?と思いました。
https://github.com/dysangelist/scala_kansai_demo/blob/master/api/src/main/scala/dysangelist/demo/logic/LibraryLogic.scala#L25
https://github.com/dysangelist/scala_kansai_demo/blob/master/api/src/main/scala/dysangelist/demo/logic/LibraryLogic.scala#L36
https://github.com/dysangelist/scala_kansai_demo/blob/master/api/src/main/scala/dysangelist/demo/logic/LibraryLogic.scala#L55
僕なら、まずBookには貸し出し状態を持たせません。Book自体が貸借を表現するモデルではないためです。Bookは貸し借りされるリソースであるので、以下のようにBookRentalのようにBookの貸し出しを表現するモデルを別途用意するのが妥当でしょう。そして、BookRentalに対して何ができるのか、具体的なユースケースと対になる振る舞いがあるべきです。このクラスにメソッドを定義していけば、Bookの貸し出しに関する知識は凝集されて、貧血症を回避できます。(ちなみにコードは脳内IDEなのでコンパイルできる前提です:P)
object BookRental {
def borrowBook(id: BookRentalId, book: Book, user: User, date: ZonedDateTime): BookRental = {
require(user.isActivtate)
require(book.isAvailable)
BookRental(id, user.id, book.id, None, Some(date), None, None)
}
}
case class BookRental (
id: BookRentalId
userId: UserId
bookId: BookId
reservedAt: Option[ZonedDateTime]
borrowedAt: Option[ZoneDateTime]
returnDueDate: Option[ZonedDateTime]
returnedAt: Option[ZonedDateTime]
) {
def status: BookRentalStatus = if (reservedAt.nonEmpty) BookRentalStatus.Reserved
else if (borrowedAt.nonEmpty) BookRentalStatus.Borrowed
else if (returnedAt.nonEmpty) BookRentalStatus.Returned
else throw new IllegalStateException("...")
def returnBook(user: User, book: Book, date: ZonedDateTime = ZonedDateTime.now): BookRental = {
require(user.id == userId)
require(book.id == bookId)
copy(returnedAt = Some(date))
}
// snip
}
LibraryLogic
は実質ユースケースなので、以下のようにすればよいと思います。つまり、ドメインオブジェクトに振る舞いを移したとしても、for式を使って代数的に演算を合成することは可能ですね。やったね!
class BookRentalUseCase[F[_]](implicit ME: MonadError[F, Error])(
userRepository: UserRepository[F], // implicit ME
bookRepository: BookRepository[F], // implicit ME
bookRentalRepository: BookRentalRepository[F] // implicit ME) {
def borrowBook(userId: UserId, bookId: BookId): F[BookRental] =
for{
user <- userRepository.findById(userId)
book <- bookRepository.findById(bookId)
bookRental <- bookRentalRepository.findByBookId(bookId)
.flatMap{ _ => ME.raiseError(...) }
.recover { case ex: NotFoundException => BookRental.borrowBook(generateId, user, book) }
} yield bookRental
def returnBook(user: User, book: Book): F[BookRental] =
for {
user <- userRepository.findById(userId)
book <- bookRepository.findById(bookId)
bookRental <- bookRentalRepository.findByBookId(bookId)
newBookRental <- ME.pure(bookRental.returnBook(user, book))
} yield newBookRental
}
circeから学ぶ Generic Programming 入門
スライド資料のリンク: https://speakerdeck.com/shnmorimoto/circekaraxue-bu-genericprogrammingru-men-scalaguan-xi-summit-2018
森本真一さん(@shnmorimoto)による発表。Shapelessを使ったGeneric Programming入門のセッション。型クラスの基本から始まり、HListを使って型クラスインスタンスの自動生成をどのように実装するか解説されていました。丁寧でわかりやすい内容でした。
ケースクラスの構造をキー・バリューのリストに変換する(LabelledGeneric)程度でしか使ったことがなかったのですが、もう少し使い所あるな、と改めて実感しました。たとえば、リポジトリ内部で利用される、DTOと集約の相互にマッピング処理を行うIso型クラスのインスタンスを自動導出できそうだと思いました。
ZOZOSUITの裏側はScalaで動いてるよ!
スライド資料のリンク:見つからない。どこ?
資料みたら感想を思い出すかもしれない…。
Akkaを分散トレーシングで見てみよう
吉田雅史さん(@grimrose)による発表。
OpenTracingは知らなかったので勉強になりました。そして、JAEGERのアイコンそんな可愛くないような…w
OpenTracingが仕様を統一し、実装は各プロダクト依存ということなのですが、OpenCensusも似たような目標があるように見えるのですが、どういう使い分けで選ぶのか基準を聞きそびれた。ScalaだとKamonを使っている企業が多いと思うのでが、Kamonとも比較できるとよさそう。
PHPエンジニアによるScalaエンジニアへの転身とその手引き
うちチームの原田くんのセッション。
ChatWorkではPHPエンジニアがScalaエンジニアに転身することを推奨しています。その一事例としての紹介です。27ページにあるとおり、基礎としての言語学習のあとに、習作課題の取り組みを経てからプロダクト参加してもらっています。「段階的に学ぶ」(一度に多くのことを学ばない)ということが、習得の難易度を下げることに成功しています。
こういった教育もしくは採用のフィードバックに、興味があればお声がけください。
Scalaでつくる証券会社とスタートアップ
スライド資料のリンク:https://speakerdeck.com/mura_mi/securities-and-startup-with-scala
村上さんのセッション。
スタートアップでありながら証券会社でもあるFOLIOさんのプロダクトをScalaがどう支えているかという話。僕自身、もっとも刺激を受けたセッションでした。
マイクロサービスアーキテクチャで構成されていて、20サービスのうち18がScalaで実装されている。バックエンドエンジニアが15名。すごい!んですが、マイクロサービスって組織と表裏一体になるので、15名は1名ずつ、1サービス・チームに配属されても3名足りません。1サービス・チームにバックエンド1名って結構つらいなーと思ったりしました。実際の組織図がどうなっているかわからないですが、最後の方のスライドに「(これから)組織を作ろうとしています」となっていたので、マイロサービスアーキテクチャに対して、組織が追従できていないように思えました。(実際は違うかもしれませんが)
逆コンウェイ戦略という方法では、まず先に組織をToBeに変形させてから、マイクロサービスを追従させるようです。これを可能な限り小さいバッチサイズで行い、フィードバックを得ながら改善できるといいのかもしれないという話を、社内でして盛り上がりました。
次に面白かったのが、ドメイン知識をScalaを使って表現できること。ドメイン上で扱う概念をcase class
などを使って実装に紐づけていて、損益の差という有益な知識も振る舞いとして定義され、結果もプリミティブ型でなく独自型として表現されていました。これは本当にすばらしい使い方ですね。ビジネス上の重要な知識はそれぞれの小さなドメインオブジェクトに集約されていくイメージができました。特にScalaはオブジェクト指向と関数型の、複数のモデリングパラダイムを持っているので、表現の幅が比較的広いという話をされていて、これも僕が以前から考えていたことなので、禿同でした!
Scalaでのドメインモデリングのやり方
スライド資料のリンク:https://speakerdeck.com/j5ik2o/scaladefalsedomeinmoderingufalseyarikata
手前味噌ですが、僕のセッションです。
実装者の視点で、ドメインモデリングをやるためのノウハウを詰め込んだ内容です。ドメインオブジェクトを見つける・作る・育てるという観点でまとめてあります。
なぜ、ドメインモデルを実装する言語にScalaが向いているかという話も少ししたのですが、ちょっと説明足らずだったところもあるので、以下補足を書いておきます!
- DDDの多くの文脈ではオブジェクトパラダイムが前提として語られることが多い
- デフォルトが不変であるカルチャと関数型言語機能が、ドメインの振る舞いを予測可能な設計(実装の詳細まで把握せずとも、設計がどのようになっているか予測できる)に導きやすい
- オブジェクト上に、純粋なドメインロジック群を容易に合成できる。つまり、ドメインロジックをスケーラブルにプログラミングできる。
まぁ、1)は他のオブジェクト指向言語でも同じだけど、2)3)も組み合わせると、より特徴的になってくるかもしれません。
現場から以上です!