Doma2 で JSON 型を使う

魔がさして、RDBMSJSON 型を導入しようとしてみました*1

前提条件

データ構造

create table customers
(
    customer_id   varchar(255) not null
        constraint customers_pkey
            primary key,
    customer_code varchar(255) not null,
    customer_name varchar(255) not null,
    attribute     json
);

エンティティクラス、ドメインクラス

Doma での エンティティクラスは以下のような感じです。

@Entity
@Table(name = "customers")
@Data
public class Customer {

  @Id
  @Column(name = "customer_id")
  private String customerId;

  @Column(name = "customer_code")
  private String customerCode;

  @Column(name = "customer_name")
  private String customerName;

  /** 永続化時に JSON カラムにするプロパティ. */
  @Column(name = "attribute")
  private Attribute attribute;
}

Attribute class は Domaドメインクラス相当です。こいつをまるっと JSON 型に入れたり取り出したりしちゃおうというのが今回のゴールです。

@Data
public class Attribute {

  @JsonProperty("memo")
  private String memo;

  @JsonProperty("age")
  private Integer age;

  @JsonProperty("point")
  private Long point;
}

手順

1. DomainConverter 作成

Attribute に対する DomainConverter を作成します。 型パラメータの String は JSON 文字列になります。

@ExternalDomain
public class AttributeConverter implements DomainConverter<Attribute, String> {

  private static final ObjectMapper MAPPER = new ObjectMapper();

  @Override
  public String fromDomainToValue(Attribute attribute) {
    try {
      return MAPPER.writeValueAsString(attribute);
    } catch (JsonProcessingException e) {
      throw new RuntimeException(e);
    }
  }

  @Override
  public Attribute fromValueToDomain(String value) {
    try {
      return value == null ? null : MAPPER.readValue(value, Attribute.class);
    } catch (JsonProcessingException e) {
      throw new RuntimeException(e);
    }
  }
}

2. DomainConverters に追加

DomainConverter を @DomainConverters に登録します。 今回のサンプルでは DomainConverterProvider クラスで管理しています。 この辺Doma に教えてます。

3. Dao インターフェース作成

最初の CustomerDao はこんな感じでした。

@Dao
public interface CustomerDao {

  @Select
  @Sql("SELECT * FROM customers WHERE customer_id = /*customerId*/'customer_001'")
  Optional<Customer> selectByCustomerId(String customerId);

  @Insert
  int insert(Customer customer);
}

4. 実行

テストクラスから実行します。

PostgreSQL で実行した所、

org.seasar.doma.jdbc.SqlExecutionException: [DOMA2009] The SQL execution is failed.
PATH=[null].
SQL=[insert into customers (customer_id, customer_code, customer_name, attribute) values ('b9d9900c-27da-4d88-b9a6-028b1a1d32ae', 'CUSTOMER_001', 'NAME_001', '{"memo":"memo_001","age":30,"point":343}')].
The cause is as follows: org.postgresql.util.PSQLException: ERROR: column "attribute" is of type json but expression is of type character varying
  ヒント: You will need to rewrite or cast the expression.
  位置: 98
The root cause is as follows: org.postgresql.util.PSQLException: ERROR: column "attribute" is of type json but expression is of type character varying
  ヒント: You will need to rewrite or cast the expression.
  位置: 98

どうも INSERT 時の JSON 型の扱いがよくないようです。

今度は H2 で実行してみましょう。

java.lang.RuntimeException: com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `examples.domain.Attribute` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('{"memo":"memo_001","age":30,"point":343}')
 at [Source: (String)""{\"memo\":\"memo_001\",\"age\":30,\"point\":343}""; line: 1, column: 1]

SELECT の結果(JSON 文字列)を受け取って Javaインスタンスを生成する所に問題があるようです。 これは困った。

5. RDBMSJSON 型を使う時

どうも、JSON 型を使う時は TEXT 型のように扱うのではダメなようです。

ということは、RDBMS 毎に SQL を変更しないといけないのか...

6. sql ファイルを用意する

Doma 2 では、SQL ファイル名をつけ分けることによって使い分けができそうです。素敵!

insert-h2.sql

-- H2 用の JSON カラムを含む INSERT
insert into customers (
    customer_id,
    customer_code,
    customer_name,
    attribute
) values (
    /* customer.customerId */'id_001',
    /* customer.customerCode */'code_001',
    /* customer.customerName */'name_002',
    /* customer.attribute */'{"hoge"::30}' FORMAT JSON
)

insert-postgres.sql

-- PostgreSQL 用の JSON カラムを含む INSERT
insert into customers (
    customer_id,
    customer_code,
    customer_name,
    attribute
) values (
    /* customer.customerId */'id_001',
    /* customer.customerCode */'code_001',
    /* customer.customerName */'name_002',
    /* customer.attribute */'{"hoge"::30}'::json
)

CustomerDao#insert を SQL ファイルを使用するように設定しなおします。

@Dao
public interface CustomerDao {

  @Select
  @Sql("SELECT * FROM customers WHERE customer_id = /*customerId*/'customer_001'")
  Optional<Customer> selectByCustomerId(String customerId);

  @Insert(sqlFile = true) // sqlFile=true 追加
  int insert(Customer customer);
}

7. 再度実行

テストクラスから実行します。 どちらも通りました。やりましたね。

まとめ

PostgreSQL だと JSON 型より JSONB 型を使いそうですが、JSON 文字列として JSON 型にデータを入れる、JSON 型からデータを取り出すことができました。

実際に JSON 関数を使った生 SQL 発行するパターンは未検証ですが、RDBMS 毎にちがうでしょうし、運用時に SQL 叩くことを想定したら TEXT 型よりも JSON 型の方が楽だろう、というモチベーションだったのでアプリケーションからは JSON 関数を意識した SELECT 文は発行しないとは思うので、必要があったら検証することにします。

H2 と PostgreSQL 両方に対応しようとしてたのは、

  • Repository の Unit テスト / Integration テストは H2 を使って早く CI を回す
  • 本番は PostgreSQL

の構成でやりたかったからです。ただ、INSERT の時点で RDBMS を意識しないといけないのはちょっとアレなので、Unit テストも PostgreSQL で CI した方が良いのかなーと思い始めました*2

コード

ここにおきますここから fork しました。Doma2 素敵です。

*1:アンチパターンだというのはごもっともなのですが、それはそれで

*2:テストクラスが増えてって、CircleCI の parallelism 増やした時に、その分 PostgreSQL コンテナが立ち上がると思うのでリソース足りるのかなーというのが心配

Mock を極力使わないようにしていた私が Mockito を使わないとムズムズするカラダになった理由

2020.05.28 追記: やっぱり mock 使わない方が良さそうに思えてきましたので、この記事から考えが変わってます

はじめに

以前、私の著書では

時間が限られた中、本番に使用しない Mock オブジェクトを作成するよりも、依存する実クラスを使用してテストケースを増やした方が時間もかからず、効率的にテストが行えると思いませんか?

とか書いていたのですが、最近はそうでもなくなっているので書き散らかしてみようと思います。 (10年以上前に書いた奴ですからね...しょうがない)

前提条件

  • SpringBoot 使った Kotlin アプリのサーバサイド(Java でも同じことができます)
    • UseCase とか Repository とか layer に分けて DI します

どんなテストでムズるか?

例えば Controller がこんな流れだったとします。

http://www.plantuml.com/plantuml/png/SoWkIImgAStDuKfCBialKd04aLnWKa7NJi6vA3Mn93KaiJZRiI3JEJ-lf2W_9oUrY0kxE9gMqE9KvyJYL0KhXMIu65TUVacgGb5cUaQ9bG9CmPFTIr_Ex7tSkEvf-xBd4zeUDwv_shdfMSji1PpssEXYjQSejRWWFwyu5U81X2fC3pYavgK0VGq0

CreateTaskApiController の責務として、CreateTaskUseCase#createTask の呼び出し & 結果によるレスポンスのハンドリングがあると思います。 (SpringBoot だと Bean Validation を使った validation も責務になりますがここでは割愛します)

CreateTaskApiController では受け取ったリクエストパラメータを元に CreateTaskUseCase#createTask のパラメータを生成します。 パラメータ生成処理自体はメソッド化なり他のクラスに処理を移譲することで Unit テストが容易に書けるでしょう。

ただ、CreateTaskController が CreateTaskUseCase#createTask に対して「想定通りのパラメータを設定したか?」の観点でテストを行う時はどうでしょう? UseCase 以降でパラメータをそのまま永続化するのであれば永続化したデータを参照することで確認はできるかもしれません。 ですが、Controller の Unit テストなのに永続化したデータまで意識するのは責務を超えている感があります。

Mock を使う

そこで Controller の Unit テストに Mockito を使って UseCase を Mock 化します。

コードはこちらです

// setup
val taskUseCaseResult = TaskUseCaseResultFixtures.create()
whenever(createTaskUseCase.createTask(any())).thenReturn(taskUseCaseResult)

↑は、引数関係なく呼び出されたら TaskUseCaseResult インスタンスを返す Mock として定義しています。

で、Controller 呼び出しを行います。 (Controller なので MockMvc 経由で呼び出していますが、UseCase の Unit テストであればメソッド呼び出しで構いません)

事前に定義した Mock が想定した結果を返すのは当たり前なので Mock の呼び出しを verify します。 観点としては、Mock の引数が想定通りか? と言う点です。

// mock のパラメータに対する verify
argumentCaptor<CreateTaskUseCaseParameter>().apply {
    verify(createTaskUseCase).createTask(capture())
    val capturedParameter = firstValue
    val expectedParameter = parameter.toParameter()
    assertThat(capturedParameter).isEqualTo(expectedParameter)
}

↑ capture キャプチャすると、Mock 呼び出し時点のインスタンスが取れるので、そのインスタンスが想定通りか verify を行います*1

この観点が抜けていると、ただテストを通しているだけになるので価値が半減します。any() でお茶を濁さないようにしましょう*2。 mock 使ったら引数の verify も忘れずに。

また、インスタンスのプロパティ1つずつ verify するとプロパティ増えた時にテストの追従を忘れる可能性があるので、まるっと比較する方が後からテスト壊したことに気付きやすくなるのでオススメです*3

テストクラスのメンテナンスコストを意識すると...

Controller の Unit テストをするなら、Controller の責務を超えた verify は避けた方が良いと思います。

API 呼び出しから一連の流れは Integration テストで行うべきで、この時は永続化した内容、発行したイベント等を verify する必要があるでしょう。 Controller の Unit テストなのに実クラスを DI していると RDBMS の構造が変わってしまった時にテストクラスにも影響を及ぼします。

これはテストクラスの肥大化にもつながり、テストクラスのメンテナンスコストが余計にかかります。

Unit テスト / Integration テストそれぞれの観点のテストクラスを作る必要があると思っているので、

  • Unit テスト: Mock をフルに使い、テスト対象クラスのメソッドだけ意識する
    • Repository の Unit テストに関しては、実際の RDBMS (やコンテナ上の DynamoDB Local とか S3Mock)にアクセスする
  • Integration テスト: 必要な箇所だけ Mock にして API 呼び出しから、レスポンスや永続化したデータの verify を行う

とした方が、ケース毎にどこまでやるのか考えずに済むと思います。

まとめ

「呼び出し先のメソッドのパラメータを正しく渡せているか」を気に始めたら、Mock 好きじゃなかった私も Mock 使わないとムズムズするようになった、と言うお話でした。

これは Controller だけでなく、UseCase や Application Service、Domain Service でも同じことが言えます。 他言語の Mock フレームワークで capture 相当の機能ってあるんでしょうか?あれば他言語でもストレスなく開発できそうだなぁ。

とはいえ、Mockito の version up に追従しなきゃいけなくなった時は「できるだけ使わない」に戻りそうですけどね...

*1:このケースは素直に verify すれば良いのですが、capture 使うサンプルとして書きたかった

*2:自戒も込めて

*3:equals メソッドの実装に影響を受けるのでケースバイケースかもしれません

【読了】ドメイン駆動設計 モデリング/実装ガイド

https://booth.pm/ja/items/1835632

「エリック・エヴァンスのドメイン駆動設計」や「実践ドメイン駆動設計」でわかった気になったけど実際にどうすれば良いかいまいちイメージがつかなかった人に是非読んでもらいたいです。 QA 形式で実際に実践する時に躓きそうなポイントも書いてあるので身近に詳しい人がいなくても始められると思います。相談相手として良い本です。

CQRS の概念をわかりやすくまとめてくれている点でも読む価値ありです。

「CQRS = データソース分離」と思われることがありますが、これは誤解です。

「CQRS = イベントソーシング」も誤解です。

誤解してました。気づかせてもらいました。

本書を一通り読んだ後に「エリック・エヴァンスのドメイン駆動設計」や「実践ドメイン駆動設計」を読むと「そういうことか」となる点が多いんじゃないかと思います。 プロジェクトにドメインモデリングを適用してみたい、と思った時にメンバー間の認識合わせとして最初に読んでおく本として最適だと思います。

本当に DDD で難しいのはドメインエキスパートの見極めと会話だと思うのですが、開発する上でこのようにしておけば、変更があった時の影響範囲を少なくすることができると考えています。

業務ロジックを抱え込んで肥大化しているトランザクションスクリプトな XXXService をメンテするのに限界を感じる前に手を打ってみませんか?

JaSST'19 Niigata に参加してきた

JaSST'19 Niigata が長岡で開催されたので参加してきました。 お堅い空気のイベント久しぶりでした。

S1) 基調講演

レビューのキキメ ~あなたのレビューは何のため?

レビューは静的なテストの1つであるという所はなるほどなぁと思いました。最近レビューしてても意味あるのかいまいち確信無いままやりがちだったのでちょっと気を引き締めることができました。

レビューの目的

人間は間違えるんです。でも間違いが少ないものを作らなければならないという明らかに矛盾した作業を強いられています。

レビューの前は、関係者の頭の中ではイメージが似てるんだけどちょっとずつ違う状態だったのを レビューが終わった時にイメージの認識合わせができていることがレビューの目的のひとつです。

「レビュー対象や背景を理解」し、その上で「問題点や認識違いの検出」を行い、関係者による認識を共有することをレビューでやっていることとのことでした。なるほど。

「あの人しか知らない」状況を減らすことが組織としてもあるべき姿ですね。

レビューのキキメを得るために

  • 無駄なレビューをしない
    • 出来るだけリアルタイムフィードバックに近づける
      • 作ってからレビューして結果を反映のサイクルを短く
    • レビュー対象は小さく、フィードバックを得やすいように
  • ぼんやりレビューをしない
    • 人間特性を考慮してレビューのやり方を設計する
      • A さんはこの観点で見て、B さんは別の観点で見て、的な感じ -> みんなが同じ観点で見ても指摘が重複するので意味が薄れるし、「他の人が見てるから自分がちょっとくらい手を抜いても良いだろう」と思ってしまいがち
  • レビューをやりっぱなしにしない
    • 指摘したこと、それを反映したことを確認する

時間は大事なリソースです。何人かで寄ってたかってだらだらやってレビューした気になるよりも、集中して効果的なレビューができるようにしたいものです。

対象の質が悪いとレビューのパフォーマンスが落ちる

確かにその通りです。誤字脱字あるとそればっかりに目がいってしまって、本質的なレビューができません。レビュー開始基準を設けてそれを満たさないとレビューしないのは良いことかもしれません。

コードもそうで、意味不明な処理のまとまりとか可読性が悪いとレビューに時間がかかると言うかあんまり見たいとは思いませんからね。気をつけよう。

人間特性

レビューする側もされる側も人間なので、正論を投げ合うのと面倒だなと思うものです。不具合の指摘も「見つけたから直せ」だと正しくてもちょっと感情的になります。建設的にできれば良いですよね。

こんな感じで意識すると良さそうです。

  • レビュアーが行うのはレビューイに対する支援
    • 粗探しではなく、より良い成果物を作成する為に行う
    • 可能な限り相手が受け取りやすいようにコメントする(やらされる -> 自分から進んでやるように)
  • 断定的な物言いをしない
    • お互いバックグラウンドが違う
    • e.g. 「もしかすると XX かもしれないと私が感じたので確認してもらえます?」
  • 手段よりも事実や目的にフォーカスする

S2) 事例紹介

エンタープライズシステムでのレビューによる品質向上

いしばしさんの発表でした。重厚な SIer での取り組みをお話ししてくれました。

ソフトウェア信頼度成長曲線とか書いてましたけど、集計するのが大変でそれ専用の人がいないとツラくて、燃えてるプロジェクトに参画していると特に「こんな集計するよりもやることない?」とか思い出し胸が熱くなりました。

とは言え、プロジェクトの状況を客観的に計測する事は大事で、何を使うかはさておき、「うまくいってる」「ちょっとまずそう」「ヤバイ」がわかるようにしておくのは必要な気がしました。計測する為の情報を出したりこねくり回して数値化するのがツライんですけどね...

S3) 事例紹介

実践コードレビュー

ウォーターセルの松井さんの発表で、レビューも組み込んだ開発フローをお話ししてくれました。

わかりみしかない発表で、この辺は組織自体を変えないと上手くいかないし、成功するかは組織による感じがするので銀の弾丸ではないとは思いますが、上手くまわれば多大な効果を発揮するやり方だと思います。

中でも

  • 自分のことは棚に上げてレビューする
  • 忖度しない
    • わからないことは遠慮しないで聞く
  • 指摘は具体的に
    • ふわっとした指摘はお互い疲弊する

この辺りは頷くしかありませんでした。

ウォーターセルさんみたいに組織内で何でも言える空気って割と当たり前に考えてましたけど、そうでもない組織も多いんだろうな...。思ってるのは自分だけ、みたいにならないようにしよう。

フロントエンドの自動テストをどうやってるか聞きたかったけど、今回のお題(レビュー)とはちょっと離れてたので自重しました。いつか聞きたい。

まとめ

何となくやってた / 依頼してたレビューへの姿勢が少し正された気がします。 自分がどんなコメントをもらうか分析して不足している観点に気づいたり、他の人がどんなコメントしているか分析して自分が不足している観点に気づければ個人として成長できるかなーと思えた時間でした。計測してみよう。

QA 的な仕事をしてた時は、

  • 現場もレビューやテストに対して関心を持ち、開発サイクルに組み込む
    • みんな設計者であり開発者でもあり QA である
  • QA の言う事を絶対に聞く
    • 失敗の全ては QA が負う
    • スケジュールが遅れそうでも QA が No と言ったらリリースできない

のどちらかでないとうまく回らない気がしていましたが、現場がちょっとずつ QA への意識を持っている感じがして良い流れだと思っています。お互いの立場だけから意見言っても「現実わかってんの?」と言われる事はわかりきってますからね。

レビューをした人が失敗に対して責任を持つのではなく、失敗した時のフォローを組織で行う空気にしていかないとこの辺りの文化はうまく回らないと思いました。QA が強すぎるとレビューも萎縮しちゃって受け身になっちゃいますよね。目的はレビューを通すことではなくて、作ったものをリリースして対価を頂くことですから、目的を履き違えちゃいけない。

今回参加して良かったです。関係者の皆様お疲れ様でした。

Doma 2 入門

f:id:nemuzuka:20190714095600p:plain

前回 の SpringBoot2.1 + Kotlin のリポジトリDoma 2 対応しました。

いちいち Insert を書かなくて済んだり、場合によっては SQL をかけたり(SQL ファイルで定義したり、アノテーションSQL 定義したり)、項目を絞った Update が SQL 文書かずにできるのが良いですね。

FOR UPDATE を制御するのに SelectOptions を使わなければならない(?) とかちょっと癖がありますが、やりたいことは十分できます。

関連するSQL ファイルが無かったり、bind する変数がメソッドに定義されていないとエラー出すのがとてもありがたく、初めて使う人にも優しいフレームワークだと思います。

レイヤー間は interface で切っていたので、SpringJDBC 使っている箇所だけの修正で済みました。 Kotlin でも問題なく動作して、既存のテストも大した修正なしです。

IntelliJ の設定が必要だったのでそこだけ注意でしょうか。

動的に SQL 変えたいケースはロジックで組み立てるのではなく 2WaySQL が良いと思っている人にはオススメです。

JAWS-UG 新潟県 (新潟/長岡/上越 合同) + NDS第60回 に参加してきた #jawsug #jawsug_niigataken #nds60

参加 してきました。 なんと、今回は1日使ったイベントでした。

午前: ハンズオン

github.com

AWS Systems Manager 触れたので良かったです。 EC2 に入りたい時に踏み台サーバが不要になるので良い感じ。

午後: メインセッション

「EAT SLEEP DEPLOY REPEAT 〜 AWS Code n 兄弟について 〜 」@wokamoto さん

ここだけの話、クラフトビールの話で終わるかと思いましたw。 Circle CI を使っているんだけど、AWS でも提供してた奴がありましたね。 少なくともビルド職人分のリソースを別のことに使えるようにした方が トータルで幸せになれることが多い気がします。

docker image 作るまでは自動化できるけど、 デプロイすることと環境(お客さん、本番/ステージング/開発)毎の変数をどう管理すべきなのか悩ましいですよね。 (そしてそれが正しく漏れなく設定していることを検証することの難しさよ...)

「4年分アップデート EC2の最新管理ハウツー」 @uekikazuki さん

t.co

AWS = EC2 の認識の方も多いと感じているのですが、やっぱり EC2 のアップデートも多いんですよね。使われてるんだなぁと実感しました。 発表してる植木さんが一番楽しそうでした。

AWSコンテナサービス入門」@hayajo さん

speakerdeck.com

わかりやすかった。はやじょさんを SRE として呼びたい所はいっぱいあるだろうなぁ。 k8s は組織で本気でリソースを割いて取り組めないなら避けた方が良さそうなイメージを持ちました。 運用で心をすり減らす人が減って欲しい。はやじょさんマジイケメン。

AWSのサービスをできるだけ紹介する」 @sakapun さん

www.slideshare.net

お疲れ様でした。 AWS、サービスの数が多すぎますよね。全部理解するのは諦めよう。

AWSを使ったシステムのテスト方針」(nemuzuka)

speakerdeck.com

NDS 60 回のお祝いを言えたので良かったです。本当に NDS は大好きなコミュニティです。

エンタープライズシステムでの品質向上プロセス」(hiro_ishibashi さん)

SI の品質に関する知見は Web サービスとかでも必要になることを再認識しました。 契約を盾にした立ち振る舞いとかでなく、SI の知見を元に効率的に品質上げるための施策を考えたい気分です。 品質は結果でなく「作り上げるもの」。大事ですよね。 いしばしさん、JaSST で発表されるのかー。行こうかな(行こう)。

LT

「EC2でサイト構築したけど躓いたことのまとめ」(nomeshi さん)

私も AWS から少額利用料が毎月引き落とされているけど、カード会社からは連絡こないなー

「ダイアログを考える」(gonchan93 さん)

そういや、window.showModalDialog あったなぁ...。 無理して Edge 使わなくて良いんじゃないかと思う今日この頃。

AWS Summit」(yas123 さん)

AWS Summit Tokyo 2019 のレポート。すごい人だったみたいですねー。

kasacchiful さん

DeepRacer の話。3時間待って走らせたのかー。

お時間ある方はぜひ。

jawsug-niigata.connpass.com

connpass.com

感想

やっぱりAWS == EC2 みたいなイメージが強そうに思いました。 組織の問題点や成し遂げたいことに対して自分で作るにしてもAWSや他のサービス使うにしても費用対効果を考えないと運用で死ぬことになるというのは常に考えておきたいと思います。開発や構築してる時は間違いなく楽しいですけど、運用が辛いのはツライ。

Kotlin 入門

f:id:nemuzuka:20190501150617p:plain

色気づいてサーバサイド Kotlin に興味を持ち始めたので勉強してみました。

今回のコード

構成

  • SpringBoot: 2.1.4.RELEASE
  • Kotlin: 1.3.30
  • あと使用してるのはこの辺見てください

内容

JSON 返す API サーバとして作ってます。

Repositry

とりあえず SQL 実行できればいいと思ったので、使いづらいと思いながらも JdbcTemplate を使いました。

コンストラクタで JdbcTemplate をインジェクションする感じです。
ここのテストは H2 にアクセスして実際に SQL を発行します(application-unittest.properties を見てる)。テストすべきは SQL ですからね。
FlywayTest でテスト用の Fixture を入れてます。
JdbcTemplate でないとすると、選択肢としては Spring JPA なんでしょうか。ScalikeJDBC みたいなの無いかしら。

Service (依存 class を Injection するケース)

Repositry のように依存する奴は Mockito で Mock 化します。 昔は Mock のメンテコストが割と高かった気がするので使わないようにしてたのですが1、最近は簡単に使えますね。
Kotlin で Mockito 使うなら Mockito-Kotlin 使うべきですかね。 Mockito-Kotlin との出会いのきっかけ

Controller

依存する奴は Mockito で Mock 化するパターンで。
正常パターンは大丈夫っぽいですが、パラメータの validation とかは Server 立ち上げないとできないのが、アレだなと思いました(Kotlin 関係ない)。

Controller のパラメータとか

この辺が Kotlin でよかったと思えるところでしょうか。
data class にのアノテーションでちょっとハマりました(field: の辺り)。
JSON にしても@JsonProperty 効かなくて焦りました。 field: との出会いのきっかけ

body パラメータの validation がアノテーションで定義できるのは嬉しいですけど、Controller 呼び出し時のエラーハンドリングが面倒なのは皆さまいかがお過ごしでしょうか(Kotlin 関係ない)。

Integration

server 立ち上げて実際に API 呼び出すパターンです。
H2 にアクセスして SQL 発行します(application-integrationtest.properties)。

レスポンスの JSON の verify に JsonAssert 使ってるのですが、最近の人は何を使っているのでしょうか。org.hamcrest.Matchers 使うのがアレな気分です。

感想

  • Kotlin について
    • Java + lombok より書きやすい気がします
    • 名前付き引数 + data class の copy 最高
    • @Slf4j は欲しいなぁ...
    • 明示的に open にしないと継承できないとか固くかけるのが良い
    • null 安全の為に Java のライブラリ使うとき面倒だな、と思うこと多し。!! 使いすぎないようにしないと
  • 結構 GitHub に Spring + Kotlin のサンプル上がってますね。Kotlin 好きが多いなw
  • Kotlin 自体はとっつきやすいです。ただ、Spring と Scala が仲良しならなぁ...という思いが隠せません
  • ktlint 入れて CI 回したり、Docker image がビルドできなくてハマった時間の方が長かったです
  • エルビス演算子 ?:命名センスに惚れた。でも他の言語でも使ってた

読んだ本

Kotlinイン・アクション

Kotlinイン・アクション

Kotlin Webアプリケーション 新しいサーバサイドプログラミング

Kotlin Webアプリケーション 新しいサーバサイドプログラミング


  1. EasyMockとかね…