Slide 1

Slide 1 text

サマーインターンシップ 2019 で 学生と DDD な Scala 開発に取り組んだ Scala 秋祭り @yoshiyoshifujii 1 / 50

Slide 2

Slide 2 text

タイトルですが、正確には… Chatwork では、サマーインターンシップを 今年 (2019 年 ) 、初めて開催しまして、その中 で、学生の皆さんと、とある Web アプリケー ションを、ドメイン駆動設計 して Scala で 開発することに取り組みました。 になります。 サマーインターンシップ2019 で学生とDDD なScala 開発に取り組んだ Scala 秋祭り 2 / 50

Slide 3

Slide 3 text

その過程で改めて ドメイン駆動設計 と Scala を Unlearn ( 学びほぐし ) する機会となりました そのあたりをご紹介できればと思います サマーインターンシップ2019 で学生とDDD なScala 開発に取り組んだ Scala 秋祭り 3 / 50

Slide 4

Slide 4 text

「まなびほぐし(アンラーン unlearn )」というのは、「まなび(learn )」のやり直しである。しか し、「やり直し」と言っても、これまで学んできた知識や技能を「帳消しにする」などということが できるわけはない。「一たす一は二である」という知識を、あえて「なかったことにする」わけには いかない。ここはやはり、これまでの「まなび」。通して身に付けてしまっている「型」としての 「まなびの身体技法(まなび方)」について、それをあらためて問い直し、「解体」して、組み替え るということを意味しているのであろう。(『まなびを学ぶ』62 頁) サマーインターンシップ2019 で学生とDDD なScala 開発に取り組んだ Scala 秋祭り 4 / 50

Slide 5

Slide 5 text

Yoshitaka Fujii @yoshiyoshifujii Chatwork 株式会社(13 ヶ月目) Scala 関西 Summit スタッフ(4 年目) 登壇 ScalaMatsuri2016 Scala でドメイン駆動設計に真正面から取り組んだ話 ScalaMatsuri2017 Serverless Architecture をScala で構築するぞ ScalaMatsuri2019 実践 Clean Architecture ⛰ 自己紹介 サマーインターンシップ2019 で学生とDDD なScala 開発に取り組んだ Scala 秋祭り 5 / 50

Slide 6

Slide 6 text

サマーインターンシップ2019 で学生とDDD なScala 開発に取り組んだ Scala 秋祭り 6 / 50

Slide 7

Slide 7 text

https://2019.scala­kansai.org サマーインターンシップ2019 で学生とDDD なScala 開発に取り組んだ Scala 秋祭り 6 / 50

Slide 8

Slide 8 text

あじぇんだ どんなことしたの? Scala の教育 DDD とScala で開発 サマーインターンシップ2019 で学生とDDD なScala 開発に取り組んだ Scala 秋祭り 7 / 50

Slide 9

Slide 9 text

どんなことしたの ? 講義パート : 1 週間 業務パート : 2 週間 サマーインターンシップ2019 で学生とDDD なScala 開発に取り組んだ Scala 秋祭り 8 / 50

Slide 10

Slide 10 text

講義パートの目的 業務パートを過ごすうえで、最低限必要となる知識を伝える 業務パートを過ごすうえで、初めて聞く単語を、限りなく少くしたい 「あ、これ、あそこで習った( 聞いた) ことやから、そこから調べれば何とかなる」状態を目指す サマーインターンシップ2019 で学生とDDD なScala 開発に取り組んだ Scala 秋祭り 9 / 50

Slide 11

Slide 11 text

講義パートの概要 ソフトウェア開発の全体像 要件定義 設計 開発・テスト 配置 運用 インフラ チーム開発 サマーインターンシップ2019 で学生とDDD なScala 開発に取り組んだ Scala 秋祭り 10 / 50

Slide 12

Slide 12 text

講義パートの概要 + 観点 ソフトウェア開発の全体像 ←Chatwork の開発の全体の流れ 要件定義 ← RDRA をベースとした、かとじゅんによる講義、みっちり2 時間。贅沢。 設計 ← DDD をベースとした、かとじゅんによる講義、みっちり4 時間。贅沢。 開発・テスト ← 後程、詳細に 配置 ← Chatwork のCI/CD を中心に 運用 ← Chatwork の運用を中心に一般的な座学 インフラ ← Chatwork で使うインフラを中心に紹介 チーム開発 ← Scrum/ カンバン/ モブの座学とワークショップ サマーインターンシップ2019 で学生とDDD なScala 開発に取り組んだ Scala 秋祭り 11 / 50

Slide 13

Slide 13 text

業務パート とあるWeb アプリケーションの開発業務 Scrum / カンバン / モブワーク などを取り入れたモダンなチーム開発を実践する Scrum チームの一員となる Dev チーム ( インターン生) Product Owner ( 社員) Scrum Master ( 社員) ステークホルダーに、Product Manager ( 社員) PM が作成した PRD (Product Requirements Document) の簡易版をインプットする Scrum チームで、要件定義、設計、開発、テスト、配置 を目指す サマーインターンシップ2019 で学生とDDD なScala 開発に取り組んだ Scala 秋祭り 12 / 50

Slide 14

Slide 14 text

Scala の教育 サマーインターンシップ2019 で学生とDDD なScala 開発に取り組んだ Scala 秋祭り 13 / 50

Slide 15

Slide 15 text

Scala の教育 ­ 前提 インターン生は、Scala の経験が、 無い 関数型経験者はいる (OCaml 、Rust 、Elm など) サーバサイドに興味がある チーム開発をしてみたい Scala を業務でどう取り組んでいるのか知りたい これを機会にScala はじめたいという方々… サマーインターンシップ2019 で学生とDDD なScala 開発に取り組んだ Scala 秋祭り 14 / 50

Slide 16

Slide 16 text

インターンが始まるまでに1 ヶ月弱 何か、事前に学んできていただく必要がある 実践Scala 入門をプレゼント 可能な限り読んできてね!分からないところあったら聞いてね! Using Akka HTTP https://doc.akka.io/docs/akka­ http/10.1.9/introduction.html#using­akka­http 分からないところあったら聞いてね! Scala の教育 ­ 事前 サマーインターンシップ2019 で学生とDDD なScala 開発に取り組んだ Scala 秋祭り 15 / 50

Slide 17

Slide 17 text

インターンが始まるまでに1 ヶ月弱 何か、事前に学んできていただく必要がある 実践Scala 入門をプレゼント 可能な限り読んできてね!分からないところあったら聞いてね! Using Akka HTTP https://doc.akka.io/docs/akka­ http/10.1.9/introduction.html#using­akka­http 分からないところあったら聞いてね! ノーリアクションで… 不安… Scala の教育 ­ 事前 サマーインターンシップ2019 で学生とDDD なScala 開発に取り組んだ Scala 秋祭り 16 / 50

Slide 18

Slide 18 text

Scala の教育 ­ 講義 サマーインターンシップ2019 で学生とDDD なScala 開発に取り組んだ Scala 秋祭り 17 / 50

Slide 19

Slide 19 text

Scala の教育 ­ 講義 目的 実践Scala 入門 と 業務パートで必要となる知識の間を埋める 業務パートで使う技術スタックを説明 実際に開発してもらうプロジェクトのScala コードを解説 ハンズオン形式で実際にScala のコードを書いてもらう サマーインターンシップ2019 で学生とDDD なScala 開発に取り組んだ Scala 秋祭り 17 / 50

Slide 20

Slide 20 text

Scala の教育 ­ 講義 Scala による開発 実践Scala 入門は、あくまでScala という言語全般についての幅広い知識を得るもの 実際の現場では、より実践的であることが求められる コードの読みやすさ コードの保守性 非機能要件とのトレードオフ Scala は色々な書き方ができてしまうので、機能要件と非機能要件を満たす範囲で、出来るだけ読み やすく( 理解しやすく) 、保守性の高いコードにする サマーインターンシップ2019 で学生とDDD なScala 開発に取り組んだ Scala 秋祭り 18 / 50

Slide 21

Slide 21 text

Scala の教育 ­ 講義 Scala を実践的に使う チーム開発を意識したコード 型を意識したコード 既存のライブラリを上手に利用する case class を上手に利用する 汎用的な型を特化した型にするため、型に対するパターンマッチ タプルを上手に利用する 値の変換の過程で、可読性を損なわない範囲で trait を上手に利用する インターフェースの観点、再利用の観点、責務の分離の観点 サマーインターンシップ2019 で学生とDDD なScala 開発に取り組んだ Scala 秋祭り 19 / 50

Slide 22

Slide 22 text

DDD と Scala で開発 サマーインターンシップ2019 で学生とDDD なScala 開発に取り組んだ Scala 秋祭り 20 / 50

Slide 23

Slide 23 text

DDD と Scala で開発 ドメインモデリングからのUnlearn Scala に関する指摘事項からのUnlearn サマーインターンシップ2019 で学生とDDD なScala 開発に取り組んだ Scala 秋祭り 21 / 50

Slide 24

Slide 24 text

ドメインモデリング ユースケースを定義し、内容に齟齬が無いか、PM と議論し、合意形成 ユースケースの内容を議論するなかで、ユビキタス言語の正しい表現に着目 ドメインモデルに採用するユビキタス言語をPM と合意 ユビキタス言語をドメインモデルとして定義するにあたり既存のドメインモデルとの関連を検討 検討したドメインモデルの関連について社員がレビュー ドメインモデルの関連にいたった根拠やユースケースをP­R に記載 そこから歴戦の猛者である先輩社員たちによる思考のトレースと可能性の模索 Dev チーム( インターン生) は、その考察から自分たちで落とし所を探しドメインモデルを確定 ドメインモデルをコードに落としてレビュー サマーインターンシップ2019 で学生とDDD なScala 開発に取り組んだ Scala 秋祭り 22 / 50

Slide 25

Slide 25 text

サマーインターンシップ2019 で学生とDDD なScala 開発に取り組んだ Scala 秋祭り 23 / 50

Slide 26

Slide 26 text

サマーインターンシップ2019 で学生とDDD なScala 開発に取り組んだ Scala 秋祭り 24 / 50

Slide 27

Slide 27 text

サマーインターンシップ2019 で学生とDDD なScala 開発に取り組んだ Scala 秋祭り 25 / 50

Slide 28

Slide 28 text

サマーインターンシップ2019 で学生とDDD なScala 開発に取り組んだ Scala 秋祭り 26 / 50

Slide 29

Slide 29 text

ユースケースを見てみる 利用者はサインインしたUserAccount を使ってMessage をBookmark のリストに1 件追加する 利用者はサインインしたUserAccount を使ってBookmark のリストを表示する 利用者はサインインしたUserAccount を使ってBookmark のリストの表示順を変更できる 利用者は、Bookmark のリストを表示したとき、サインインしたUserAccount を使ってMessage を Bookmark のリストから削除する サマーインターンシップ2019 で学生とDDD なScala 開発に取り組んだ Scala 秋祭り 27 / 50

Slide 30

Slide 30 text

ユースケースを見てみる 利用者はサインインしたUserAccount を使ってMessage を Bookmark のリスト に1 件追加する 利用者はサインインしたUserAccount を使って Bookmark のリスト を表示する 利用者はサインインしたUserAccount を使って Bookmark のリスト の表示順を変更できる 利用者は、 Bookmark のリスト を表示したとき、サインインしたUserAccount を使ってMessage を Bookmark のリスト から削除する サマーインターンシップ2019 で学生とDDD なScala 開発に取り組んだ Scala 秋祭り 28 / 50

Slide 31

Slide 31 text

サマーインターンシップ2019 で学生とDDD なScala 開発に取り組んだ Scala 秋祭り 29 / 50

Slide 32

Slide 32 text

サマーインターンシップ2019 で学生とDDD なScala 開発に取り組んだ Scala 秋祭り 30 / 50

Slide 33

Slide 33 text

サマーインターンシップ2019 で学生とDDD なScala 開発に取り組んだ Scala 秋祭り 31 / 50

Slide 34

Slide 34 text

サマーインターンシップ2019 で学生とDDD なScala 開発に取り組んだ Scala 秋祭り 32 / 50

Slide 35

Slide 35 text

サマーインターンシップ2019 で学生とDDD なScala 開発に取り組んだ Scala 秋祭り 33 / 50

Slide 36

Slide 36 text

サマーインターンシップ2019 で学生とDDD なScala 開発に取り組んだ Scala 秋祭り 34 / 50

Slide 37

Slide 37 text

サマーインターンシップ2019 で学生とDDD なScala 開発に取り組んだ Scala 秋祭り 35 / 50

Slide 38

Slide 38 text

サマーインターンシップ2019 で学生とDDD なScala 開発に取り組んだ Scala 秋祭り 36 / 50

Slide 39

Slide 39 text

ドメインモデリングからの Unlearn ユースケースの文脈から、「Bookmark のリスト」が読み取れたため、永続化を意識せずにモデリン グすると、 BookmarkList を定義したい ドメインモデリングにおいては、永続化層を意識せずに実施する その後、どのように永続化するか、クエリの要求も踏まえて永続化層の設計をする 永続化層の検討では、データの制限の有無、データストアの種類など、様々な制約を考慮する 最終的には、ドメイン層以外のフィードバックもドメインモデリングに影響を与える サマーインターンシップ2019 で学生とDDD なScala 開発に取り組んだ Scala 秋祭り 37 / 50

Slide 40

Slide 40 text

CQRS+ES を使うと、後半の 永続化層の様々な制約をある 程度考慮せずにドメインモデ リングが可能 ちなみに … サマーインターンシップ2019 で学生とDDD なScala 開発に取り組んだ Scala 秋祭り 38 / 50

Slide 41

Slide 41 text

Scala に関する指摘事項から Unlearn Future 関連 生成ロジックとするか事後条件とするか サマーインターンシップ2019 で学生とDDD なScala 開発に取り組んだ Scala 秋祭り 39 / 50

Slide 42

Slide 42 text

lazy val bookmark = Bookmark( id = bookmarkId, status = BookmarkStatus.Active, messageId = command.messageId, userAccountId = command.userAccountId, createdAt = now ) val result = for { message <- messageRepository.findById(command.messageId).recoverWith { case e: AggregateNotFoundException => Future.failed(new MessageNotFoundException(e)) } thread <- threadRepository.findById(message.breachEncapsulationOfThreadId).recoverWith { case e: AggregateNotFoundException => Future.failed(new ThreadNotFoundException(e)) } _ <- Future { if (!thread.hasMember(command.userAccountId)) throw new AddBookmarkForbiddenException("You are not a m } _ <- bookmarkRepository.store(bookmark) } yield { AddBookmarkSuccess(bookmarkId) } サマーインターンシップ2019 で学生とDDD なScala 開発に取り組んだ Scala 秋祭り 40 / 50

Slide 43

Slide 43 text

Future 関連 ­ 指摘事項 Future.apply は並行処理としてスケジューリングされてしまいますが、ここは大した計算処理ではな いのでスレッド実行せずに同期処理でよい Future#successful, failed でよい Future の中で明示的に例外をthrow しないようにしましょう。Future.failed を使う if 式は必ずelse 句書く。if は式なので必ず値を返す必要がある。命令型プログラミングとしてelse 句を 書かない場合もあるが、本来の使い方としては値を返すべき _ <- Future { if (!thread.hasMember(command.userAccountId)) throw new AddBookmarkForbiddenException("You are not a m } サマーインターンシップ2019 で学生とDDD なScala 開発に取り組んだ Scala 秋祭り 41 / 50

Slide 44

Slide 44 text

Future 関連 ­ コードで提案 before after _ <- if (!thread.hasMember(command.userAccountId)) Future.failed(new AddBookmarkForbiddenException("You are not a member!")) else Future.succcessful(()) _ <- Future { if (!thread.hasMember(command.userAccountId)) throw new AddBookmarkForbiddenException("You are not a m } サマーインターンシップ2019 で学生とDDD なScala 開発に取り組んだ Scala 秋祭り 42 / 50

Slide 45

Slide 45 text

正常系をベースに書く _ <- if (thread.hasMember(command.userAccountId)) Future.succcessful(()) else Future.failed(new AddBookmarkForbiddenException("You are not a member!")) 正常系を先に書く Thread が Member を持っているなら、正常。それ以外は、異常。 否定の ! は、「持っている」の否定なので「持っていない」になるな、と脳内変換が必要 「読みやすさ」を重視するなら、否定とか無いほうが良い どうしても異常を先に検査するなら、 thread.hasNotMember とかにする サマーインターンシップ2019 で学生とDDD なScala 開発に取り組んだ Scala 秋祭り 43 / 50

Slide 46

Slide 46 text

生成ロジックとするか事後条件とするか lazy val bookmark = Bookmark( id = bookmarkId, status = BookmarkStatus.Active, messageId = command.messageId, userAccountId = command.userAccountId, createdAt = now ) val result = for { message <- messageRepository.findById(command.messageId).recoverWith { case e: AggregateNotFoundException => Future.failed(new MessageNotFoundException(e)) } thread <- threadRepository.findById(message.breachEncapsulationOfThreadId).recoverWith { case e: AggregateNotFoundException => Future.failed(new ThreadNotFoundException(e)) } _ <- if (thread.hasMember(command.userAccountId)) Future.successful(()) else Future.failed(new AddBookmarkForbiddenException("You are not a member!")) _ <- bookmarkRepository.store(bookmark) } yield AddBookmarkSuccess(bookmarkId) サマーインターンシップ2019 で学生とDDD なScala 開発に取り組んだ Scala 秋祭り 44 / 50

Slide 47

Slide 47 text

生成ロジックとするか事後条件とするか _ <- if (thread.hasMember(command.userAccountId)) Future.successful(()) else Future.failed(new AddBookmarkForbiddenException("You are not a member!")) 上記の処理は、Bookmark の生成ロジックと言えそう こんな感じで、生成処理をFactory Method としてドメインに持っていくのが良いかなーと思いま す。 object Bookmark { def generate(thread: Thread, messageId: MessageId, userAccountId: UserAccountId): Either[DomainError, Bookma if (thread.hasMember(userAccountId)) { ... } サマーインターンシップ2019 で学生とDDD なScala 開発に取り組んだ Scala 秋祭り 45 / 50

Slide 48

Slide 48 text

サマーインターンシップ2019 で学生とDDD なScala 開発に取り組んだ Scala 秋祭り 46 / 50

Slide 49

Slide 49 text

サマーインターンシップ2019 で学生とDDD なScala 開発に取り組んだ Scala 秋祭り 47 / 50

Slide 50

Slide 50 text

Scala に関する指摘事項から Unlearn Future の並行処理を適切に使おう Future で明示的な例外をthrow しない if は式なので、else 書く 生成ロジックを暗黙知にせず、ファクトリメソッドに明示する サマーインターンシップ2019 で学生とDDD なScala 開発に取り組んだ Scala 秋祭り 48 / 50

Slide 51

Slide 51 text

まとめ サマーインターンシップを通して、社内でも暗黙知となっている部分をUnlearn することができた 改めて学び直すこと、言語化することを通して、色々な気付きがあるのは良いですね 引き続き、このような機会を創出していくことを、社内やコミュニティで実現できると良いですね サマーインターンシップ2019 で学生とDDD なScala 開発に取り組んだ Scala 秋祭り 49 / 50

Slide 52

Slide 52 text

一緒に働くエンジニアを募集しています! https://corp.chatwork.com/ja/recruit/ サマーインターンシップ2019 で学生とDDD なScala 開発に取り組んだ Scala 秋祭り 50 / 50