Slide 1

Slide 1 text

Sansan株式会社 部署 名前 Connect × Server-Side Kotlinで実現する! スキーマ駆動開発と品質改善の実践 Sansan技術本部 Sansan VS サイボウズ - 品質向上Tips冬祭り Sansan株式会社 技術本部 Bill One Engineering Unit 市川 達裕

Slide 2

Slide 2 text

写真が⼊ります 2022年 Sansanに中途⼊社。前職では企業向けのSaaS企業にて、 バックエンド・インフラ領域を主軸にサービス開発に注⼒。 Sansanでは、インボイス管理サービス「Bill One」にてWebア プリケーション開発を⾏う中、チーム内外の技術的な改善をリー ドしている。 市川 達裕 Sansan株式会社 技術本部 Bill One Engineering Unit

Slide 3

Slide 3 text

Outline - About Bill One - ⽇々のチーム開発と課題 - Connect × Server-Side Kotlin (Ktor)

Slide 4

Slide 4 text

About Bill One

Slide 5

Slide 5 text

Bill Oneは、Sansan株式会社が提供するインボイス管理サービスです。 郵送やメールといったさまざまな⽅法・形式で届く請求書をオンラインで⼀括受領し、素早く正確にデータ化。 請求書をクラウド上で⼀元管理することで、アナログで⾮効率な請求書業務をデジタル化します。 インボイス制度や電⼦帳簿保存法にも対応し、⽉次決算業務を効率化することで、企業経営における意思決定のスピードを加速します。 ※⽉次決算業務 毎⽉の営業成績、財政状況を明らかにするために⾏われる業務。経理担当者が⾏う業務で、毎⽉の数字の締め処理作業として発⽣します。

Slide 6

Slide 6 text

技術スタック フロントエンド / BFF サーバーサイド インフラ データベース ドキュメント React express ツール chromatic GitHub Copilot

Slide 7

Slide 7 text

アーキテクチャ Email Cloud Load Balancing Backend Cloud Run Database Cloud SQL Static Files Cloud Storage Cloud Tasks API Gateway Cloud Load Balancing API Client User Logging Error Reporting Cloud Build Bill One Entry Management / Developer Tools Cloud Functions Monitoring Authentication Auth0 Login Screen Frontend / BFF Cloud Run Pub/Sub

Slide 8

Slide 8 text

- Large Scale Scrum (LeSS) Huge をベースとした開発体制 - プロダクト領域ごとに、複数の機能開発チームが紐づく 開発体制 プランニング リファインメント 開発 テスト リリース Area PB デモ ⁃ Area PdM ⁃ Designers ⁃ Engineers ⁃ Area PdM ⁃ Designers ⁃ Engineers ⁃ Area PdM ⁃ Designers ⁃ Engineers Product Backlog (PB) Area PB Area PB Area PB

Slide 9

Slide 9 text

⽇々のチーム開発と課題

Slide 10

Slide 10 text

- 開発着⼿のタイミングでEvent Stormingやドメイン・API設計を実施 - Backend / Frontendの実装は同⼀チーム内で進⾏ - Bill One のエンジニアはフルスタックエンジニア - 複数⼈で並⾏実装するケースも多い - APIスキーマ未導⼊のため、情報共有の⼿段はチームごとに様々 ⽇々のチーム開発 Event Storming 設計 Backend 実装 Frontend 実装 開発開始 デモ

Slide 11

Slide 11 text

- Frontend - Backend間のAPIスキーマがズレる - チーム内の連携が上⼿く取れていないと頻発 - 最悪の場合、バグやインシデントに繋がるケースも存在 - Request Validationを都度実装している - ⼀定の実装コストがかかる - 後からAPIの仕様を知るには、実装を読み解く必要がある - 個々⼈のコードを読む⼒に⼤きく依存 - 今後、開発組織を更に拡⼤していくうえでの障壁になりかねない 課題 スキーマ駆動開発を実践すれば解決するのでは!

Slide 12

Slide 12 text

Connect × Server-Side Kotlin (Ktor)

Slide 13

Slide 13 text

- Buf Technologies, Inc. により開発 - gRPC互換のHTTP APIを実装するためのフレームワーク - HTTP/1.1で通信可能 - Proxy不要でWeb Frontendから直接呼び出せる - gRPCと同じようにProtocol Buffersを⽤いてAPI定義 - Protobufのエコシステムが活かせる - 2024/4 CNCF Sandbox Projectに採択 - Go, TypeScript, Swift, Kotlinサポート - Swift, KotlinはClient対応のみ Connectとは

Slide 14

Slide 14 text

- Backendサービスの多くはKotlin + Ktorで構成されている - 機能開発の⼿を⽌めず、段階的にスキーマ駆動開発を採り⼊れたい - アプリケーションやインフラの構成にはあまり⼿を加えたくない - YAMLではなく、静的型付けのあるスキーマ⾔語が望ましい - Goで書かれたサービスでのConnect導⼊実績がある Bill Oneの状況 なんとかConnectをKotlin + Ktor上で活⽤できないものか...

Slide 15

Slide 15 text

提供されているもの - Connect Client (主にAndroid App想定) - Protobuf MessageのSerializer ⾜りていないもの - Connect Server - Unary RequestをProtoc⽣成ClassへDeserialize - Unary ResponseをJSON Serializeして返却 Connect-Kotlin MessageのSerialize / Deserializeさえ扱うことができれば、 Ktor ServerでもConnect ProtocolのUnary Requestを受けられそう

Slide 16

Slide 16 text

Connect-Ktor (https://github.com/ichizero/connect-ktor) - Ktor Content Negotiation Plugin向けのProtobuf Serializerを提供 - Accept / Content-Type Headersを元にRequest / Responseを処理 - ProtobufからKtorのRouting定義を⽣成するProtoc Pluginを提供 Connect-Ktor public interface ElizaServiceHandler { public suspend fun say(request: SayRequest, call: ApplicationCall): ResponseMessage public object Procedures { @Resource("/connectrpc.eliza.v1.ElizaService/Say") public class Say } } public fun Route.elizaService(handler: ElizaServiceHandler) { post(handle(handler::say)) } object ElizaServiceHandlerImpl : ElizaServiceHandler { override suspend fun say(request: SayRequest, call: ApplicationCall): ResponseMessage = ResponseMessage.Success( sayResponse { sentence = request.sentence }, emptyMap(), emptyMap()) } fun main() { embeddedServer(CIO, port = 8080) { install(Resources) routing { install(ContentNegotiation) { connectJson() } elizaService(ElizaServiceHandlerImpl) } }.start(wait = false) } ⽣成コード 実装コード

Slide 17

Slide 17 text

- 既存のREST APIと共存可能 - スキーマ駆動開発の段階的な導⼊が可能 - 既存APIを置き換える際、APIテストがほぼそのまま再利⽤できる Connect-Ktorのメリット fun main() { embeddedServer(CIO, port = 8080) { install(Resources) routing { route(“/connect”) { install(ContentNegotiation) { connectJson() } elizaService(ElizaServiceHandlerImpl) // Using Connect Protocol } route(“”) { install(ContentNegotiation) { json() } post { … } // Using REST } } }.start(wait = false) }

Slide 18

Slide 18 text

品質⾯でのメリット Event Storming 設計 ⽣成コード API スキーマ Backend 実装 Frontend 実装 - Frontend - Backend間のAPIスキーマのズレを防げる - バグやインシデントの可能性を低減 - Protocol Buffersによる安全なAPI定義 - 静的型付け - 前⽅互換性・後⽅互換性を保つための仕組み - 暗黙知を形式知に変えられる - 新規に参画したメンバーのキャッチアップコストの低減 - QAエンジニアのE2Eテスト設計に活⽤ 開発開始 デモ

Slide 19

Slide 19 text

品質⾯でのメリット - Request Validation実装の簡略化 - protovalidate (https://github.com/bufbuild/protovalidate) - ProtobufのCustom OptionsにValidationルールを書くだけで、 Runtime Validationが⾏えるスグレモノ - Go, C#, Java, Python サポート ※ protobuf-es v2にてCustom Optionsが扱えるように。 TypeScriptがサポートされる⽇は近いのかも? syntax = "proto3"; import "buf/validate/validate.proto"; message User { // User's name, must be at least 1 character long. string name = 1 [(buf.validate.field).string.min_len = 1]; } 参考: https://github.com/bufbuild/protovalidate APIスキーマ定義するだけで、 Always-ValidなRequest / Responseが実現できる

Slide 20

Slide 20 text

- スキーマ駆動開発、Connect導⼊について合意形成済み - Architecture Decision Record (ADR)に記録 - 1つのBackendサービスを対象に導⼊準備中 - 順次対象のBackendサービス・チームを拡⼤予定 現状とこれから

Slide 21

Slide 21 text

スキーマ駆動開発を段階的に採り⼊れることで 機能開発の⼿を⽌めることなく より安全で品質の⾼いプロダクトを作っていく まとめ

Slide 22

Slide 22 text

Sansan 技術本部 募集ポジション紹介 https://media.sansan-engineering.com/

Slide 23

Slide 23 text

No content