サーバサイドKotlin+GraphQLのアプリケーションをOSS化しました

これはQiita Kotlin Advent Calendar 2018 1日目の記事です。

qiita.com

ツイートしたら良い反応もらえたので、サーバサイドKotlinのアプリを以下で公開する事に決めました。

github.com

cloneして、docker-compose upしたら、GraphiQLが http://localhost:8090/graphiql 立ち上がって、サーバの動作確認が出来る。簡単!!!

f:id:shiraji:20181214165629p:plain
GraphiQL画面

Ubie内で絶賛開発中のシステムに少し手を加えていますが、基本的に実際に開発している環境と同じになっています。

公開目的

公開に至った理由はいくつかあります。

サーバサイドKotlinやりたいって人のハードルを下げたかった

やりたい!という声はいろんなところで聞くけど、「環境ないし・・・。」「経験無いし・・・。」という方も多いと思ってます。実際Ubie入社前の自分がそうでした。

このプロジェクトは、clone後すぐにローカルで動かす事が可能です。

そのため、このお手軽さを見てもらい、もっと多くの方にサーバサイドKotlinやってもらいたいなーと思いました。

Kotlin+SpringBoot+GraphQLの開発者にコードレビューしてもらいたかった。または仲良くなりたかった

今回、色々な事情があり、KotlinでGraphQLの採用をしています。自分にKotlin+GraphQLの実績がないため、コードを公開することにより、いろんな人に見てもらい、より良いものになったら良いなと思いました。さらに同じような環境の方ともっと仲良くなりたいです!

特に後述しますが、多対多のGraphQLResolverの処理が本当にこんな感じのコードで良いのかが不明です。Spring+GraphQLの情報が少なすぎる。。。もっと効率良く出来そうなんですが・・・

同じ構成のものをOSS化することにより、Ubieの入社即パフォーマンス発揮!をしてもらいたかった

今のところ、Ubieのサーバサイドに関わる人には入社前に1日インターンをしてもらい、チームに溶け込めるか?を見てもらう時間を取っています。しかし、時間が1日しかなく、環境構築に時間を取られてしまうのはもったいないです。そのため、同じ環境をOSS化することにより、先に環境構築出来る状況にしたいと考えました。

さらに将来的には1日インターン宗教上無理!って方にこのOSSプロダクト開発に携わってもらい、雰囲気をそこで味わってもらうのもいいかなと考えています。

技術スタック

このプロジェクトでは以下の技術スタックを利用しています。

今後テスト書くので、以下が追加予定

  • JUnit5
  • Mockk
  • WebTestClient

パッケージ構成

app.ubie.kotlingraphqlsample
├── domain
├── infrastructure
│   └── jdbc
├── presentation
│   ├── queryresolver
│   └── resolver
└── service

(Query)Resolver → Service → JDBCという呼び出し階層になってます。

DB

f:id:shiraji:20181214205606p:plain
E-R図

薬と傷病の関係性のサンプルテーブルになっています。医療業界で利用されているデータに合わせ、IDがなかったり、外部キーがUniqueじゃなかったり、全て多対多の仕様になっています。

DBアクセスはspringがデフォルトで提供しているJDBCを使い、SQLをコードに書いてDBアクセスしています。これは賛否両論あると思いますが、以下の理由から採用しています。

  • SQLのチューニングがしやすい
  • ラーニングコストが低い
  • 一目でどこでどのSQLを発行しているのかがわかる

Flywayを利用して自動マイグレーションが走る状態にしてあります。

GraphQL

GraphQLのスキーマ定義ファイルには薬情報と病気情報が定義されています。

type Query {
    # 薬を取得します
    drugs(yjCode: String!) : [Drug!]
    # 病気を取得します
    diseases(icd: String!) : [Disease!]
}

# 薬情報
type Drug {
    # 薬品名
    name: String!
    # YJ Code。だいたい薬を表すために使われるCode。一意ではないので注意
    yjCode: String!
    # 併用禁忌薬
    kinkiDrugs: [Drug!]
    # 処方してはいけない特定の病気
    kinkiDiseases: [Disease!]
} 

# 病気
type Disease {
    # 傷病に対してつくコード。一意ではないので注意
    icd: String!
    # 病名
    name: String!
    # 処方されてはいけない薬
    kinkiDrugs: [Drug!]
}

Queryブロックには公開APIが定義されています。graphql-javaではこの定義をGraphQLQueryResolverを実装したクラスで記述する必要があります。このプロジェクトではqueryresolverパッケージ内で定義されています。

@Component
class DiseaseQueryResolver(private val service: DiseaseService) : GraphQLQueryResolver {
    fun diseases(icd: String): List<Disease> = service.getDiseases(icd)
}
@Component
class DrugQueryResolver(val drugService: DrugService) : GraphQLQueryResolver {
    fun drugs(yjCode: String): List<Drug> = drugService.getDrugs(yjCode)
}

Drug/Diseaseブロック内にあるkinkiDrugsなどの外部キーを使った結合はgraphql-javaではGraphQLResolver<T>を実装する必要があります。resolverパッケージ内で定義されています。typeがDrugkinkiDrugsの取得時にはfun getKinkiDrugs(drug: Drug): List<Drug>が呼び出される。

@Component
class DiseaseResolver(private val service: DiseaseKinkiDrugService, private val drugService: DrugService) : GraphQLResolver<Disease> {

    fun getKinkiDrugs(disease: Disease): List<Drug> {
        val kinkiDrugs = service.findKinkiDrugsByIcd(disease.icd)
        return drugService.getDrugs(kinkiDrugs.map { it.yjCode })
    }
}
@Component
class DrugResolver(
    private val kinkiDrugService: KinkiDrugService,
    private val diseseKinkiDrugService: DiseaseKinkiDrugService,
    private val drugService: DrugService,
    private val diseaseService: DiseaseService
) : GraphQLResolver<Drug> {

    fun getKinkiDrugs(drug: Drug): List<Drug> {
        val kinkiDrugs = kinkiDrugService.findKinkiDrugsByYjCode(drug.yjCode)
        return drugService.getDrugs(kinkiDrugs.map { it.kinkiYjCode })
    }

    fun getKinkiDiseases(drug: Drug): List<Disease> {
        val kinkiDrugs = diseseKinkiDrugService.findKinkiDrugsByYjCode(drug.yjCode)
        return diseaseService.getDiseases(kinkiDrugs.map { it.icd })
    }
}

多対多な関係が多く、SQLの発行回数が若干多いなーという印象があります。この辺りどうしたらいいのか誰かと語りたい!!!

終わりに

このコード読んで、UbieでサーバサイドKotlinやってみたいと思った方は @shiraj_i に直接DMか以下のWantedlyさんのサイトから話を聞きにきて下さい!

www.wantedly.com

もっと気軽にUbieに遊びに来たい!という方は以下のBosyuさんからメッセージを送って下さい!

bosyu.me