RustでMVC・クリーンアーキテクチャっぽく書いてみた

2022/01/29 2022/01/29 #Rust

RustでMVC & クリーンアーキテクチャっぽく書いてみた

こんにちは。のんです。

久しぶりにRustの記事更新です。

何もしていなかったわけではなく、ブログにできるレベルの成果物ができていなかっただけですw。

成果物

※masterブランチで作業しているので、既に色々更新されている可能性があることにご注意ください。

個人的悩みポイント

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

My Qiita posts My Qiita contributions My Qiita followers

主にPHPを使用し、サーバーサイドを担当。最近はフロントにも興味津々。

なにかを作ったりいじったりするのが好きで、個人開発なども行っている。

趣味はバイクアイコン画像は大抵愛車の「Z250」である。友達にアイコン描いてもらえて嬉しい。

PHP / Laravel / CakePHP2 / CakePHP3 / Vue / Nuxt / C# / etc...

Tags

#のんラボ #Laravel #Vue #個人開発 #ブログ #プログラミング #javascript #Html5 #WEBサービス #Twitter #今年の抱負メーカー #勉強方法 #PWA #モバイルアプリ #Android #ツーリング #バイクに乗るエンジニア #Z250 #秋吉台 #能登半島 #バイク #冒険 #東尋坊 #Squid #リバースプロキシ #hosts #axios #cropper #AdSense #Bootstrap #MySQL #高速化 #トドTask #Telescope #デバッグ #composer #テスト #セキュリティ #POSレジ #スマレジ #本部機能 #バリデーション #入力チェック #Mac #Chrome #テスト駆動開発 #開発手法 #UI #デザイン #WEBサイト #機能美 #PHP #Laravel 6 #コメント #バージョンアップ #vue-cli #localhost #BIツール #売上分析 #TANAX #MFK250 #ツアーシェルケース2 #RESTful #API #REST API #実務的 #PHP Tech Tutor #Smaregi Tech Talk #勉強会 # ブログ #CakePHP3 #CSRF #VSCode #開発環境 #CakePHP3.0 #さくらのレンタルサーバー #モジュールモード #シェル #メール #Gmail #relay #OGP #エラーページ #抱負 #家庭教師 #ドメイン駆動設計 #DDD #読書会 #那智の滝 #伊勢志摩 #伊勢志摩スカイライン #フロント #三方五湖 #レインボーライン #ボーイスカウト・ルール #プログラマが知るべき97のこと #リファクタリング #ユビキタス言語 #車輪の再発明 #マイクロサービス #デプロイ #QA #laravel-mix #Tips #storybook #@storybook/addon-actions #昇降デスク #コードレス #書斎 #オフィス #リモートワーク #働き方 #エラーハンドリング #スマレジ4 #pixel 5 #レビュー #スマレコ #TDD #RSS #404 #高山ダム #ラーツー #React #Nuxt #node_modules #エラー #インポート #設定方法 #環境構築 #Docker #フォレストパーク神野山 #学生向け #PR #採用 #Node.js #npm #しまなみ海道 #youtube #CSS #IE #SLA #Rust #千里浜なぎさドライブウェイ #千里浜 #インサイドセールス #曽爾高原 #無線LAN #ポートフォリオ #バルカンS #納車 #Next.js #チームビルディング #リーダー #悩み #Github Actions #Marp #ネタ #サーバー移行 #S3 #ストレージサーバー #RFC 7807 #Digest #認証 #Xdebug