RustでMVC・クリーンアーキテクチャっぽく書いてみた
2022/01/29 2022/01/29 #RustRustでMVC & クリーンアーキテクチャっぽく書いてみた
こんにちは。のんです。
久しぶりにRustの記事更新です。
何もしていなかったわけではなく、ブログにできるレベルの成果物ができていなかっただけですw。
成果物
※masterブランチで作業しているので、既に色々更新されている可能性があることにご注意ください。
https://github.com/nonz250/rust-sampleを見る
https://github.com/nonz250/rust-sample
個人的悩みポイント
mod.rs
について
Rustはmoduleの考え方あので、modを定義する必要があるのですが、クリーンアーキテクチャを適用するとどうしてもファイル数が多くなります。
その際、 mod.rs
というファイルをたくさん作ることになるのですが、これをどうにかスマートに書く方法がないものかなやんでしまいました。
結局、 mod.rs
を作りまくるこで対応しましたが、もう少しいい感じに書く方法はありそう。。
今後に期待。
エラーについて
Rustをやるにあたって一番最初に躓くところだそうです。
特に私はPHPerなので、例外ガンガン書くタイプのプログラマーでした。
Golangなども普段業務で触っているわけではないので、例外がないプログラミング言語をどのように書けばいいのかめっちゃ困りました。
DBアクセスができなかった...とか、データが存在しないとか、ランタイム系のエラーはWEBアプリには付き物なので、通る前提でコーディングできないのが辛いところです。
例外を投げるというより、エラー型?( Result
型)を返すという方がニュアンスとして合っていそうなのでそのように実装していますが、エラーのバケツリレーが始まった感出ていてありゃ〜という感じ
この問題もどうにかしてスマートに書く方法はありそう。
現在は泥臭くエラー返しています。。。がこの方法も悪くはないのでは?とブログ書きながら考えていたりもします。
PostRepository
の実装部分であるこことか
fn find_by_id(id: PostIdentifier) -> Result<Post, String> {
let connection = establish_connection();
let post = match posts_schema::dsl::posts
.find(id.to_string())
.first::<PostModel>(&connection) {
Ok(ret) => ret,
Err(_err) => return Err("Post not found".to_string())
};
return Ok(Post::new(
PostIdentifier { identifier: Ulid::from_string(&post.id).unwrap() },
PostTitle::new(post.title).unwrap()
))
}
それを呼び出すユースケース、こことかの話。
impl UpdatePost {
pub fn process(input: UpdatePostInput) -> Result<(), String> {
let mut post = match PostMySqlRepository::find_by_id(input.identifier) {
Ok(post) => post,
Err(err) => return Err(err)
};
post.change_title(input.title);
PostMySqlRepository::save(post);
return Ok(());
}
}
ぜーんぶResult返すようになっちゃいました。
ここまででRustいいなって思ったところ
強力な安全性
これはRustに限ったことではありませんが、やっぱり強力な型安全、処理安全なところですね。
ValueObject
は採用していますが、これ書く意味ある?(いや、当然書く意味はあるのですが。)と感じてしまうくらい、Rustの仕様を信じてコーディングできるのはすごいなと思いました。
あと、エラー周りも最初は戸惑いましたが、エラーを投げるということすらも 「これくらいのエラーは想定しておけよ」 と言われているようで、 ストイックな言語である ということがヒシヒシと伝わってきます。私は好感持てました。
CQRSでReadModelを実装したときはほぼほぼRustの仕様に乗っからせてもらっています。
#[derive(Serialize)]
pub struct PostReadModel {
identifier: String,
title: String
}
そのほかの実装部分
pub struct GetPostMySqlQuery {}
pub struct GetPostMockQuery {}
pub trait GetPostQuery {
fn get_post() -> Vec<PostReadModel>;
}
impl GetPostQuery for GetPostMySqlQuery {
fn get_post() -> Vec<PostReadModel> {
let connection = establish_connection();
let results = posts_schema::dsl::posts
.load::<Post>(&connection)
.expect("Error loading posts");
let mut posts = Vec::new();
for result in results {
posts.push(PostReadModel {
identifier: result.id,
title: result.title
})
}
return posts;
}
}
impl GetPostQuery for GetPostMockQuery {
fn get_post() -> Vec<PostReadModel> {
return Vec::from([PostReadModel {
identifier: Ulid::new().to_string(),
title: "mock".to_string()
}]);
}
}
serde = { version = "1.0", features = ["derive"] }
ただの構造体をReadModelとして定義して、jsonにシリアライズできるようにしています。
これだけストイックなら、コードを短く書こうと書いている側も頑張るはず
これは個人の感想ですが、ここまでストイックな言語ですと、書けば書くほどエラーに直面します。
冗長なコードがだらだら書くとエラーに当たりやすくなるので、短く簡潔に必要なものだけを書くように方向性を矯正させられるとも考えました。
とりあえず動けば良かろうをすると、大変な目に合いますねw
最後に
とはいえ、まだまだ基礎中の基礎しか実装てきていないので、このリポジトリで色々勉強していきたいと思います。
そのほかにUlid
のライブラリを導入したりしていましたが、この辺は書くまでもないことなので割愛しています。
多分ここまでくればシンプルなWEBアプリは書けるようになっているはずですが、メールの送信とか、 curl
の実行くらいは勉強しておきたい。
「rocket使え」ってのはなしでw
FW使うと勉強の意味無くなってしまいますから。
また進捗あれば記事にします。
そのときはよしなに。
.
のん
所属 : 株式会社スマレジ 開発部
YouTube : のんラボ
Twitter : @nonz250
Github : nonz250
Qiita : @nonz250
主にPHPを使用し、サーバーサイドを担当。最近はフロントにも興味津々。
なにかを作ったりいじったりするのが好きで、個人開発なども行っている。
趣味はバイクアイコン画像は大抵愛車の「Z250」である。友達にアイコン描いてもらえて嬉しい。
PHP / Laravel / CakePHP2 / CakePHP3 / Vue / Nuxt / C# / etc...
Tweet
Tweets by nonz250Past articles
2022/05/15 S3みたいなストレージサーバーっぽいものを自前で用意する⑤【DI / Repogitory実装】2022/04/24 S3みたいなストレージサーバーっぽいものを自前で用意する④【RFC 7807 エラーレスポンス実装】
2022/04/10 S3みたいなストレージサーバーっぽいものを自前で用意する③【Digest認証実装】
2022/03/26 S3みたいなストレージサーバーっぽいものを自前で用意する②【ミドルウェア実装】
2022/03/13 S3みたいなストレージサーバーっぽいものを自前で用意する①【ルーティング初期設定】
2022/02/11 Next.jsのGetServerSidePropsの挙動について
2022/01/29 RustでMVC・クリーンアーキテクチャっぽく書いてみた
2022/01/07 2022年の抱負