🚀

RustのRocketとSqlxでクリーンアーキテクチャっぽいやつを作ってみました

2023/09/25に公開

RustのRocketとsqlxでクリーンアーキテクチャっぽくして、ディレクトリとか
モジュールもなるべくいい感じに分けつつ、テストもしやすい・モックも作りやすい状態を、頑張って作ってみました。

以前Axumでも同じようなことをやりました

以前Axumでも同じようなことをやってみていました。ワイの歴代最多スター数を誇るリポジトリが下記です。

https://github.com/net3i/rust-axum-sqlx-sample

このリポジトリのAxumのバージョンはもう結構古いですので、今やろうとしたら全体的に修正が必要だとは思います。あとは、use_caseがTraitになっていないので、モックが作りづらいとかはあるかなあと思います。

ただ、コードは、(私的にはですが)割とシンプルな感じなので、シンプルさ的にはいいかなあと思っています。

ただ、リポジトリに渡しているのが、DB Poolなので、リポジトリの関数を実行する度にPoolからコネクションをとってきていると思います。これが、今回のRocketのバージョンとの違いかなあと思っております。

毎回関数実行時にconnectionを取得・破棄するというのは、1リクエストで同じconnectionを使い続ける場合と比べて、相対的に非効率になるのかなと思ってます。(計測したりしていませんが、ChatGPTに聞いたら、そうだよ、と言っていました)また、トランザクションも基本的にはrepository関数内で完結させるしかないので、複数のrepositoryをまたいだり、外部サービスのレスポンスを待ってからcommitしたりということが、基本できない(ややこしい)のかなと思っています。

今回作ったRocketバージョンのリポジトリ

今回のRocketバージョンのリポジトリが下記です。よかったらスターをお願いします!

https://github.com/net3i/rust-rocket-sqlx-sample

特徴

  • use_caseもrepositoryもTraitにしたので、モックが作りやすくなりました。
  • repositoryの各関数に渡すのは、DB Poolから取得したコネクションの参照になっていますので、上記のAxumバージョンより、ちょっと効率がよいのではないか?と思っています。
    • 1リクエスト毎にPoolからコネクションを取得して、同じリクエスト内ではずっとそのコネクションを使います。リクエストの処理が終わったら返却(破棄)されます。
  • repositoryの各関数にconnectionを渡しますので、use_case側でトランザクションを作って、複数のrepository関数の実行後にcommitさせるというのも、やろうと思えばできます。
  • controller, use_case, repository(統合テスト)が簡単に出来るようになっています。

悩ましかった点

  • トランザクションの扱い
    • TransactionPoolConnectionの両方を受け取れるrepository関数を使いながらmockall.automockを使うのが難しかった(結局両方受け取れる関数にするのをやめた)
    • 上記の結果、repositoryのテスト時にTransactionのRollbackを使ってテーブル状態をクリアするというのが使えなくなった。(今はテスト前にtruncateしている)
  • Axumバージョンと比べるとDBコネクション周りのコードが若干シンプルではなくなった。
    • まあでも関数にPoolを渡すと非効率だし、トランザクションも使いづらいのでよいのかなと思いました。

将来やりたいこと

  • SeaORMを導入したい。
  • テストでトランザクションを簡単に使えるようにしたい。
  • 現在、Rocketのsqlxは0.6ですが、SeaORMは0.7です。もしかしたら、Rocketのdb関連ライブラリを使うのをやめるかも。

まとめ

  • AxumもRocketもいい感じだと思いました
  • トランザクションややこしいと思いました
  • よかったらリポジトリのスターをお願いします!

Discussion