Slide 1

Slide 1 text

Scala関西 Chatwork株式会社 FUJII Yoshitaka コードをどまんなかに据えた モデリング - Scala版

Slide 2

Slide 2 text

自己紹介 - FUJII Yoshitaka - @yoshiyoshifujii - Chatwork株式会社 - Scala関西 - 登壇 - ScalaMatsuri2019 - 実践 Clean Architecture - 趣味 - ⛰ ⛺ ‍♂ 庭DIY

Slide 3

Slide 3 text

アジェンダ 1. 課題感 2. JIG 3. sbt-jig 4. sbt-jig-tutorial 5. コードへのフィードバック例 3

Slide 4

Slide 4 text

01 課題感

Slide 5

Slide 5 text

課題感 - モデリングどうやりますか - ホワイトボードに書く - ツールを使う - astah - PlantUML - Miro 5

Slide 6

Slide 6 text

課題感 - 図に書いた通りに実装できていますか - 認識はズレていませんか - コードとドキュメントは同期されていま すか - どうやって担保しますか 6

Slide 7

Slide 7 text

課題感 - 図とコードとテストの変更のリズムは どうですか - さくさく変更できますか - 図からコード、コードをテスト、 コードを変更ってぐるぐるしてると、 図は…あとで良いや ってなりがち 7

Slide 8

Slide 8 text

02 JIG

Slide 9

Slide 9 text

コードをどまんなかに - コードに軸足を置く - 最初はホワイトボードとかで認識合わせ る(Miroはいいぞ  ) - 意図を込めてコードを書き、意図が表現 されたドキュメントを見る 9

Slide 10

Slide 10 text

コードをどまんなかに 10 https://speakerdeck.com/irof/kodowodomannakaniju-etashe-ji-apuroti

Slide 11

Slide 11 text

コードをどまんなかに - JIG Java Instant-document Gazer (ドキュメントは一時的に見るもの) - https://github.com/dddjava/jig - JIGは、バイトコードおよびソースコードから、 一覧(Excel形式)やダイアグラム(SVG形式)を 出力するツールです 11

Slide 12

Slide 12 text

JIG 三層+ドメインモデル のアーキテクチャで実 装されたコードから分 析・設計情報を出力す るツール 12

Slide 13

Slide 13 text

JIGのDiagramたち 13 https://jigzag.work/

Slide 14

Slide 14 text

03 sbt-jig

Slide 15

Slide 15 text

Scalaに対応した - JIGにScalaのソースコードを解析できるように拡張部 分を追加させていただいた - https://github.com/dddjava/jig/pull/424 - https://irof-web.s3.amazonaws.com/jig/master/use-case- and-fellows.svg 15

Slide 16

Slide 16 text

sbt-jigを作った - https://github.com/yoshiyoshifujii/sbt-jig - Scalaのソースから、Scaladocを読み取り、別名を扱え るようにした - JIGに追加した org.dddjava.jig.domain.model.jigloaded.alias.ScalaSourceAliasReader をsbt-jig側で実装 - Scalametaを使って解析 16

Slide 17

Slide 17 text

sbt-jig - JIGのプロパティをsbtにも用意 17 override lazy val projectSettings = Seq( jigReports := Jig.jigReportsTask(jig).value, jigDocumentTypeText in jig := "", jigOutputDirectoryText in jig := "./target/jig", jigOutputOmitPrefix in jig := ".+\\.(service|domain\\.(model|type))\\.", jigModelPattern in jig := ".+\\.domain\\.(model|type)\\..+", jigProjectPath in jig := "./", jigDirectoryClasses in jig := Jig.makeClasses().value, jigDirectoryResources in jig := Jig.makeClasses().value, jigDirectorySources in jig := "src/main/scala" )

Slide 18

Slide 18 text

sbt-jig - Scalaのバージョンを見たうえで、classesが出力される ディレクトリを導出 18 def makeClasses(): Initialize[String] = Def.setting { s"target/scala-${scalaBinaryVersion.value}/classes" }

Slide 19

Slide 19 text

04 sbt-jig-tutorial

Slide 20

Slide 20 text

sbt-jig-tutorialを作った - https://github.com/yoshiyoshifujii/sbt-jig-tutorial - jig-tutorialがあったので、sbt-jig用を作った - ぜひお試しください 20

Slide 21

Slide 21 text

05 コードへの フィードバック例

Slide 22

Slide 22 text

Roleオブジェクト - DCIアーキテクチャ - https://digitalsoul.hatenadiary.org/entry/20100131/1264925022 - オブジェクト指向の本質が人間のメンタルモデルを捉え ることにあるとした上で、問題と解決方法を提示 - 問題は、構造を捉えることに長けている反面、ふるまい を捉えることが苦手 - 特定のふるまいをどのクラスにおくべきか - エンティティクラスが大量のメソッドで肥大化する - 今回は、 Factory Method について捉えた 22

Slide 23

Slide 23 text

Roleオブジェクト 23 - グループチャットにメッセージを生成する Factory Methodを実装する - グループチャットは、メッセージを生成す るうえで、集約由来の生成ロジックを保持 しており、そのロジックを通してしか、 メッセージの生成を許可しない - グループチャットのふるまいとして持たせ ることは、良さそうに思える

Slide 24

Slide 24 text

Roleオブジェクト 24 - グループチャットを通して生成 する別の集約のエンティティと して、ファイルやタスク、ライ ブ… - グループチャットが持つ Factoryとしてのメソッドが増 え、肥大化する

Slide 25

Slide 25 text

Roleオブジェクト 25 - Factory Methodは、JIGでレポートすると相互の 関連で出力される package groupChat import message.Message trait GroupChat { def createMessage(accountId: AccountId, body: String): Message = ??? } package message import groupChat.RoomId trait Message { val roomId: RoomId }

Slide 26

Slide 26 text

Roleオブジェクト 26 - メッセージパッケージにRoleオブジェクトを作る - Scalaの場合、型クラスとして作成すると扱いやすい package message import groupChat.GroupChat trait MessageRole[A] { def createMessage(self: A)(accountId: AccountId, body: String): Message } object MessageRole { implicit val groupChatMessageRole = new MessageRole[GroupChat] { override def createMessage(self: GroupChat)(accountId: AccountId, body: String): Message = ??? } implicit def toMessageRoleOps[A: MessageRole](self: A) = new { def createMessage(accountId: AccountId, body: String): Message = implicitly[MessageRole[A]].createMessage(self)(accountId, body) } }

Slide 27

Slide 27 text

働くをもっと楽しく、創造的に