Slide 1

Slide 1 text

保険SaaSのORM導入について 2022/6/17 Server-Side Kotlin Meetup vol.3 吉本和弘 1

Slide 2

Slide 2 text

自己紹介 • 吉本和弘 • 株式会社justInCaseTechnologies(2021/9〜) • 保険SaaS基盤「 joinsure 」のRecordチームのバックエンドエンジニア • Kotlin、Spring Boot(Spring WebFlux) • 入社までは • Ruby on Rails • Java、Spring Boot(Spring MVC) • Vue.js、(React.js) 2

Slide 3

Slide 3 text

保険会社 * 3 *事業会社や保険代理店 のご利用も可能 顧客 プラン選択 本人認証 告知・重要事項説明 会員資格確認 商品ページ(LP) & 申込フォーム 契約参照 異動・解約 決済 契約更新 お客様 ポータル 査定・承認 問合せ 提出書類の参照 (電子データ) 支払記録 保険金 請求フォーム クラウドベースの保険基盤をコンポーネントに分けて提供する”joinsure” © 2021 justInCase, All Rights Reserved. 何を開発しているか

Slide 4

Slide 4 text

本日の発表の流れ • 背景 • ORMの比較 • フェーズ1 • Spring Data R2DBC • フェーズ2 • Spring Data R2DBC、jOOQ • フェーズ3 • jOOQ 4

Slide 5

Slide 5 text

背景 5

Slide 6

Slide 6 text

背景 保険SaaS「joinsure」Recordの新規開発 • フレームワーク • Spring WebFlux • リアクティブプログラミングによってノンブロッキングで非同期な アプリケーションを開発できるSpring Framework • アプリケーションアーキテクチャー • ドメイン駆動設計 • CQRS(コマンドクエリ責務分離) • 更新系、参照系で処理が分離 • ORM • ???(未定) • MyBatis or Exposed or jOOQ or Spring Data R2DBC 6

Slide 7

Slide 7 text

ORMの比較 7

Slide 8

Slide 8 text

ORMの比較① • MyBatis • Pros • SQLベースで記述できる • Cons • XMLベースでSQLを記述する必要がありメンテナンスコストがかかる 8

Slide 9

Slide 9 text

ORMの比較② • Exposed • Pros • coroutine サポートがある (non-blocking) • Raw SQLを使用することができる • Spring Boot Starterがある • Cons • JSONB / JSON サポートがない • バージョンが0.x.x(安定版ではない) • R2DBCに未対応 9

Slide 10

Slide 10 text

ORMの比較③ • jOOQ • Pros • SQLライクにKotlinで記述できるのでメンテナンスしやすい • JSONB / JSON をサポート • R2DBC に対応している • Cons • R2DBCに対応している(3.15以降)が、Spring Boot Starterは3.14 • https://blog.jooq.org/reactive-sql-with-jooq-3-15-and-r2 dbc/ 10

Slide 11

Slide 11 text

ORMの比較④ • Spring Data R2DBC • Pros • Spring Dataファミリー(Spring Data JDBC、Spring Data JPA) • Spring WebFluxを使う場合は一般的 • CoroutineCrudRepositoryでCRUD処理はORMの標準メソッドで可 能 • Cons • テーブル間のリレーション(Spring Data JPAの「@OneToOne、 @OneToMany」)に対応していない 11

Slide 12

Slide 12 text

ORMを比較した結論 • Spring Data R2DBC or jOOQ • R2DBCでの実績を重視してSpring Data R2DBC • クエリ処理(join)ではORMの標準メソッドが使えなそう • R2dbcEntityTemplateなどでSQLライクに記述できそう • 問題がありそうなら、jOOQの利用を検討する • クエリ系の処理での対応 • R2dbcEntityTemplate • SQLライクにKotlinで実装することができる • joinができない • https://spring.pleiades.io/spring-data/r2dbc/docs/current/reference/html/#r2dbc.enti tyoperations • @Query、DatabaseClientを使ってSQLを記述する 12

Slide 13

Slide 13 text

フェーズ1 13

Slide 14

Slide 14 text

ORMの方針(フェーズ1) • 更新系、参照系 • XXXCommandReposity : CoroutineCrudRepository →CRUD処理をORMの機能で実現 • 一部の参照系(ORMの標準機能で実現できない処理) • XXXQueryReposity →@Query、DatabaseClientを使ってSQLを記述する  val record = TaskRecord(id = id, name=name) taskCommandRepository.save(record) interface TaskCommandRepository : CoroutineCrudRepository 14

Slide 15

Slide 15 text

フェーズ1での課題 • 静的なクエリを実行することはできるが、クエリを動的に生成できな いなさそう(@Query、DatabaseClient) • 条件の共通化ができない • タイプセーフにSQLを記述することができない →参照系ではjOOQを利用する 15

Slide 16

Slide 16 text

フェーズ2 16

Slide 17

Slide 17 text

ORMの方針(フェーズ2) • 更新系 • Spring Data R2DBC • XXXCommandReposity : CoroutineCrudRepository   →CRUD処理をORMの機能で実現 • 参照系 • jOOQ • spring-boot-starter-jooqがjOOQ:3.14でR2DBCに未対応のため、 R2DBC対応のjOOQ(3.15以降)を単体で導入 17

Slide 18

Slide 18 text

フェーズ2での課題① • CoroutineCrudRepository.saveメソッドの仕様 • Entity(TaskRecord)のprimary keyがnull→INSERT、値がある→UPDATE →アプリケーションでUUIDを生成できない →複合主キーが使えない @Table("TASKS") data class TaskRecord(val name) { @Id val id: UUID?, val name: String, } val id: UUID? = xxx val name:String = xxx val record = TaskRecord(id = id, name=name) taskCommandRepository.save(record) interface TaskCommandRepository : CoroutineCrudRepository 18

Slide 19

Slide 19 text

フェーズ2での課題② • Entityが2階層(ドメイン、永続化) @Table("TASKS") data class TaskRecord(val name) { @Id val id: UUID?, val name: String, } data class Task(val id: UUID?,val name) { xxx } ドメイン層 永続化(テーブル) class TaskQueryRepository interface TaskCommandRepository : CoroutineCrudRepository @Repository class TaskRepositoryImpl( private val taskCommandRepository: TaskCommandRepository, ) : TaskRepository • Repositoryが2階層(ドメイン、永続化) ドメイン層 永続化 19

Slide 20

Slide 20 text

フェーズ2での課題③ • 複数テーブルを更新したい →ORMの標準メソッドでは難しそう →@Query、DatabaseClientで実現する方法もあるが。。。 Spring Data R2DBCを利用するとORMの制約を受ける → ORMの制約を受けないjOOQを更新系でも利用を検討 20

Slide 21

Slide 21 text

更新系にjOOQを検討 • jOOQ(R2DBC) • トランザクションでロールバックが効かない • https://github.com/jOOQ/jOOQ/issues/12218   →更新系はノンブロッキングを諦め、    JDBC版jOOQを利用することに 21

Slide 22

Slide 22 text

フェーズ3 22

Slide 23

Slide 23 text

ORMの方針(フェーズ3) • 更新系 • jOOQ:JDBC • 参照系 • jOOQ:R2DBC 23

Slide 24

Slide 24 text

jOOQのJDBC版、R2DBC版の併用 • @Qualifier、@Beanを使って、DIするDSLContextをR2DBC、 JDBCで出し分け • jOOQで自動生成するファイルは共有可能 @Repository class TaskRepositoryImpl( @Qualifier(“R2DBC”) private val create: DSLContext ) @Configuration class JooqConfig { @Bean(name = ["R2DBC"]) fun r2dbcContext(cfi: ConnectionFactory): DSLContext { xxx } } 24

Slide 25

Slide 25 text

ORMの方針(フェーズ3):実装例 • Entity data class Task(val id: UUID,val name) { xxx } @Repository class TaskRepositoryImpl( private val create: DSLContext, ) : TaskRepository { override fun save(task: Task): Task { XXX(jOOQでSQLを記述) } } • Repository ドメイン層 ドメイン層 jOOQの自動生成ファイル 永続化 25

Slide 26

Slide 26 text

ORMの方針(フェーズ3) • 更新系 • jOOQ:JDBC • 参照系 • jOOQ:R2DBC →この方針で開発を進め、ORMについては問題はなさそう 26

Slide 27

Slide 27 text

さいごに 27

Slide 28

Slide 28 text

さいごに 効率性やスケーラビリティ重視のR2DBCと 堅牢性重視のJDBCを併用して保険SaaSを開発中 Komapperの導入に関するものだが、 ORMに関係なくJDBC or R2DBCを考える上では参考になりそう 「R2DBCをサポートするORM Komapper/Server-Side Kotlin Meetup vol.2」より https://speakerdeck.com/nakamura_to/r2dbcwosapotosuruorm-komapper?slide=16 28

Slide 29

Slide 29 text

バックエンドエンジニア募集中 - 技術スタック - Kotlin - Spring WebFlux - jOOQ - 勤務環境 - フルリモート・フルフレックスOK! - 技術顧問 竹端 尚人さん (@n_takehata) への相談会勉強会 など学習の場がある - シリーズA後期のスタートアップ×金融事業でローリスク・ハイリターン の挑戦ができる - 気になる方はこちらからエントリー (【ジャストインケース 採用】で検索!) 29

Slide 30

Slide 30 text

3 0