😈

Rust×Clean Archで今更入門するCursor爆速開発

に公開2

要件定義から本格API完成まで1.5時間の衝撃体験

🔥 はじめに:40分でプロダクションレベルAPIが完成した話

皆さん、40分でクリーンアーキテクチャを採用した本格的なECサイトAPIを構築できると思いますか?

「無理でしょ...」と思った方、私も数日前まで同じでした。

しかし、Claude + Cursorを組み合わせた開発を実際に体験してみると、従来の開発常識が完全に覆されました。

  • 📋 要件定義: Claude と30分相談
  • ⚡ 実装: Cursor で40分爆速コーディング
  • 🧪 テスト: 30分で動作確認完了
  • 合計: 約1.5時間でMVP完成

この記事では、その実体験を余すことなく共有します。

🤔 なぜ今更Rust × Clean Archなのか?

「AI開発ならPythonでしょ?」「TypeScriptが主流でしょ?」

確かにそうです。でも、だからこそ差別化のチャンスなんです。

🦀 Rust × AIの相性が抜群な理由

型システムがAIの理解を助ける

// これだけで要件が明確に伝わる
struct Product {
    id: String,
    name: String,
    price: u32,      // 負の値は型レベルで防止
    stock: i32,      // 在庫は負もあり得る
    created_at: DateTime<Utc>,
}

コンパイラエラーが具体的

error[E0308]: mismatched types
  --> src/main.rs:42:23
   |
42 |     product.price = -100;
   |                     ^^^^ expected `u32`, found integer

メモリ安全性 + パフォーマンス

  • C/C++並みの速度
  • セキュリティリスクの最小化
  • 一度動けば本当に安定

🏗️ Clean Archが威力を発揮する場面

AI開発では「設計」より「実装」に注目が集まりがちですが、実は設計こそがAI活用の成否を分ける重要な要素です。

  • 悪い例: 「商品管理システム作って」→ 設計が曖昧で迷走
  • 良い例: 「Clean Archで商品ドメインを...」→ AIが的確に実装

📋 Phase 1: Claude との要件定義 (30分)

まず、Claudeと一緒にプロジェクトの設計を固めていきます。

🎯 最初の相談

: 「Rustでクリーンアーキテクチャを使ったECサイトのAPIを作りたいです。まずはMVPとして商品管理機能を実装したいのですが、設計を相談させてください。」

Claude: 以下の構成を提案します...

🏗️ アーキテクチャ決定

src/
├── domain/           # ドメイン層
│   ├── entities/     # エンティティ
│   ├── repository/   # リポジトリトレイト
│   └── error/        # エラー定義
├── application/      # アプリケーション層
│   └── usecase/      # ユースケース
├── infrastructure/   # インフラ層
│   └── repository/   # リポジトリ実装
├── adapter/          # アダプター層
│   └── api/          # Web API
└── main.rs

🛠️ 技術スタック決定

30分の相談で以下が確定:

  • Web Framework: Axum 0.7 (高性能・型安全)
  • Database: SQLite (MVP用、後でTiDBに移行可能)
  • ORM: SQLx (コンパイル時SQLチェック)
  • Error Handling: 統一されたAppError型
  • Testing: tokio-test + integration tests

ポイント: Claudeは選択肢と理由をセットで提案してくれるので、意思決定が高速化されます。

⚡ Phase 2: Cursor爆速実装 (40分)

設計が固まったら、いよいよCursorでの実装です。

🎮 Cursorの使い方

プロジェクト初期化

cargo new lightning-clean-commerce
cd lightning-clean-commerce

Cargo.toml設定

Cursorに「Axum + SQLx + tokio でWeb API作りたい」と伝えると、即座に必要な依存関係を提案:

[dependencies]
axum = "0.7"
tokio = { version = "1.0", features = ["full"] }
sqlx = { version = "0.7", features = ["runtime-tokio-rustls", "sqlite"] }
serde = { version = "1.0", features = ["derive"] }
uuid = { version = "1.0", features = ["v4"] }
chrono = { version = "0.4", features = ["serde"] }
anyhow = "1.0"

🏗️ 実装順序

1. ドメイン層 (10分)

Product Entity:

use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Product {
    pub id: String,
    pub name: String,
    pub description: Option<String>,
    pub price: u32,
    pub stock_quantity: i32,
    pub category: Option<String>,
    pub created_at: DateTime<Utc>,
    pub updated_at: DateTime<Utc>,
}

impl Product {
    pub fn new(
        name: String,
        description: Option<String>,
        price: u32,
        stock_quantity: i32,
        category: Option<String>,
    ) -> Self {
        let now = Utc::now();
        Self {
            id: uuid::Uuid::new_v4().to_string(),
            name,
            description,
            price,
            stock_quantity,
            category,
            created_at: now,
            updated_at: now,
        }
    }
    
    pub fn decrease_stock(&mut self, quantity: i32) -> Result<(), String> {
        if self.stock_quantity < quantity {
            return Err("在庫が不足しています".to_string());
        }
        self.stock_quantity -= quantity;
        self.updated_at = Utc::now();
        Ok(())
    }
}

Repository Trait:

use async_trait::async_trait;
use crate::domain::{entities::Product, error::AppError};

#[async_trait]
pub trait ProductRepository: Send + Sync {
    async fn save(&self, product: Product) -> Result<Product, AppError>;
    async fn find_by_id(&self, id: &str) -> Result<Option<Product>, AppError>;
    async fn find_all(&self) -> Result<Vec<Product>, AppError>;
    async fn update(&self, product: Product) -> Result<Product, AppError>;
    async fn delete(&self, id: &str) -> Result<bool, AppError>;
}

2. アプリケーション層 (15分)

Cursorに「ProductUseCaseを実装して」と伝えると、バリデーション込みで生成:

pub struct ProductUseCase<R>
where
    R: ProductRepository,
{
    product_repository: R,
}

impl<R> ProductUseCase<R>
where
    R: ProductRepository,
{
    pub fn new(product_repository: R) -> Self {
        Self { product_repository }
    }

    pub async fn create_product(
        &self,
        name: String,
        description: Option<String>,
        price: u32,
        stock_quantity: i32,
        category: Option<String>,
    ) -> Result<Product, AppError> {
        // バリデーション
        if name.trim().is_empty() {
            return Err(AppError::validation("商品名は必須です"));
        }

        let product = Product::new(name, description, price, stock_quantity, category);
        self.product_repository.save(product).await
    }
    
    // その他のCRUD操作...
}

3. インフラ層 (10分)

SQLiteリポジトリ実装も一瞬:

pub struct SqliteProductRepository {
    pool: SqlitePool,
}

#[async_trait]
impl ProductRepository for SqliteProductRepository {
    async fn save(&self, product: Product) -> Result<Product, AppError> {
        sqlx::query!(
            r#"
            INSERT INTO products (id, name, description, price, stock_quantity, category, created_at, updated_at)
            VALUES (?, ?, ?, ?, ?, ?, ?, ?)
            "#,
            product.id,
            product.name,
            product.description,
            product.price as i64,
            product.stock_quantity,
            product.category,
            product.created_at,
            product.updated_at
        )
        .execute(&self.pool)
        .await?;
        
        Ok(product)
    }
    
    // その他の実装...
}

4. API層 (5分)

最後にAxumのハンドラー:

pub async fn create_product(
    State(app_state): State<AppState>,
    Json(req): Json<CreateProductRequest>,
) -> Result<Json<ApiResponse<Product>>, AppError> {
    let product = app_state
        .product_use_case
        .create_product(
            req.name,
            req.description,
            req.price,
            req.stock_quantity,
            req.category,
        )
        .await?;

    Ok(Json(ApiResponse::success(
        product,
        "商品が正常に作成されました",
    )))
}

🐛 エラー解決も高速

開発中に出たコンパイルエラーも、Cursorに貼り付けるだけで即座に解決提案:

error[E0599]: no method named `validation` found for enum `AppError`

→ Cursorが「AppErrorにvalidationメソッドを追加しますね」と即座に修正

🧪 Phase 3: テスト & 動作確認 (30分)

📝 統合テスト作成

#[tokio::test]
async fn test_create_product() {
    let app = create_test_app().await;
    
    let product_data = json!({
        "name": "テスト商品",
        "description": "説明",
        "price": 1000,
        "stock_quantity": 10,
        "category": "テストカテゴリ"
    });
    
    let response = app
        .oneshot(
            Request::builder()
                .method(http::Method::POST)
                .uri("/api/products")
                .header(http::header::CONTENT_TYPE, "application/json")
                .body(Body::from(product_data.to_string()))
                .unwrap(),
        )
        .await
        .unwrap();
    
    assert_eq!(response.status(), StatusCode::OK);
}

🚀 API動作確認

# 商品作成
curl -X POST http://localhost:3000/api/products \
  -H "Content-Type: application/json" \
  -d '{"name":"Sample Product","description":"Sample description","price":1000,"stock_quantity":5,"category":"Food"}'

# レスポンス
{
  "success": true,
  "data": {
    "id": "f534fd87-07a1-4600-9bb4-ee579969350c",
    "name": "Sample Product",
    "price": 1000,
    "stock_quantity": 5,
    "created_at": "2025-06-28T12:12:07.681777600Z"
  },
  "message": "商品が正常に作成されました"
}

🎯 得られた知見とノウハウ

💡 AI開発のコツ

段階的に進める

  • 一度に全体を作らず、層ごとに実装
  • 小さな機能単位で動作確認

型を活用する

  • Rustの型システムがAIの理解を大幅に助ける
  • エラーメッセージが具体的で修正が容易

設計を先に固める

  • Claude との要件定義時間は投資
  • 明確な設計があるほど実装が高速化

🔧 Rust特有の注意点

所有権システム

  • AIが提案するコードでも借用チェッカーに引っかかることがある
  • でも、エラーメッセージが親切なので修正は簡単

非同期処理

  • async/awaitの扱いは人間がレビュー必要
  • トレイトのasync実装はasync-traitが必須
  • 修正: Rust 1.75以降はasync fn in traitが安定化。async-traitが必要なのはトレイトオブジェクト(Box<dyn Trait>)使用時のみ

エラーハンドリング

  • Result<T, E>の一貫した使用が重要
  • カスタムエラー型の定義で開発効率アップ

📊 従来開発との比較

項目 従来開発 AI駆動開発 効率化
設計 1-2日 30分 48-96倍
実装 3-5日 40分 108-180倍
テスト 1日 30分 48倍
合計 5-8日 1.5時間 約100倍

💰 具体的なコスト換算

従来の開発工数:

  • 設計・要件定義: 8時間
  • ドメイン層実装: 8時間
  • アプリケーション層実装: 8時間
  • インフラ層実装: 8時間
  • API層実装: 4時間
  • テスト作成: 4時間
  • 合計: 40時間

AI活用での実績:

  • 設計・要件定義: 0.5時間(Claude相談)
  • 実装全体: 0.67時間(40分)
  • テスト・確認: 0.5時間
  • 合計: 1.67時間

時給5,000円で計算した場合:

  • 従来開発: 5,000円 × 40時間 = 200,000円
  • AI活用: 5,000円 × 1.67時間 = 8,350円

コスト削減: 191,650円 (95.8%削減)

フリーランス案件での現実的なケース:

案件A: 「商品管理API構築」

  • 従来見積: 40時間 × 8,000円 = 32万円
  • AI活用: 2時間で完成 → 時給換算16万円!

案件B: 「ECサイトMVP」

  • 従来見積: 200時間 × 8,000円 = 160万円
  • AI活用: 10時間で完成 → 同価格なら時給16万円

🚀 今後の可能性と展望

💼 個人開発への影響

この開発速度があれば:

  • SaaS開発: MVP を数日で完成
  • 受託開発: 従来案件を1/10の工数で
  • プロトタイプ: アイデアを即座に形に

🎯 差別化戦略

技術選択での差別化

  • みんながPython/JSを使う中でRustを選択
  • 高パフォーマンス・セキュアな API で勝負

🎯 開発速度を武器にした差別化戦略

1. 「時間価値」を前面に押し出す営業

従来の提案:

「商品管理APIを3週間で開発します。費用は80万円です。」

AI活用の提案:

「商品管理APIを今日の夕方にお渡しできます。
通常3週間かかる案件ですが、最新のAI技術により大幅な効率化を実現。
費用は同等の80万円ですが、3週間早くビジネス開始できます。
競合他社より3週間早くサービスインできる価値を考えてください。」

2. 具体的な営業シナリオ

お客さんとの商談:

  • : 「このシステム、いつ頃できそうですか?」
  • : 「要件を整理させてください...30分ほどお時間いただけますか?」
    (要件整理)
  • : 「ありがとうございます。では今日の夕方には本番環境で動いてます」
  • : 「え?今日?今日って...今日ですか?」
  • : 「はい。では開発を始めますね。コーヒーでも飲んで待っていてください」

3. 価格戦略の転換

従来モデル: 時間売り
「月単価80万円 × 1ヶ月 = 80万円」

AI活用モデル: 価値売り
「通常1ヶ月の案件を1日で完成 = 120万円」
→ 「速度プレミアム」として50%増しで請求可能

4. 実際の差別化ポイント

  • 即日対応: 「明日までに」→「今日中に」
  • 品質保証: AI活用でもクリーンアーキテクチャ採用
  • リテイク無制限: AIだから修正コストが極小
  • ドキュメント完備: コード生成と同時にドキュメントも自動生成

5. 口コミ効果の狙い

お客さんが友人・同業者に話したくなるレベルの体験:

「うちのエンジニア、1時間でシステム作るんだよ...魔法みたい」
「普通3週間の案件を夕方には完成させてた。しかもバグなし」

この口コミが新規案件獲得の最強の武器になります。

品質での差別化

  • AI活用 + 型安全性で高品質を担保
  • クリーンアーキテクチャで保守性を確保

🏁 まとめ: AI時代の新しい開発スタイル

今回の体験を通じて確信したことがあります。

「もうコードが書けないエンジニアの時代は終わった」

重要なのは:

  • 💡 何を作るべきかを見極める力
  • 🤖 AI との効果的な協働スキル
  • 🎯 ビジネス価値を理解する能力
  • ⚡ 高速で仮説検証するマインド

技術的ハードルが下がった今、本当の勝負は「設計力」と「問題発見力」で決まります。

🎉 最後に

この記事を読んで「自分にもできそう」と思った方、ぜひチャレンジしてください!

Cursor月額$20、Claude$20の合計$40で、従来の100倍の開発速度を手に入れられます。

カフェ代10回分で人生変わるかもしれません。使わない理由が見つからないレベルです。

AI時代の開発、一緒に楽しみましょう! 🚀

📚 参考リンク

🏷️ タグ

#Rust #Cursor #Claude #AI開発 #クリーンアーキテクチャ #Web API #高速開発

Discussion

kanaruskanarus

トレイトのasync実装はasync-traitが必須

は言い過ぎではないでしょうか?

async_fn_in_trait が stable に入った 1.75 以降は、async fn を含む trait を dyn で使いたいときくらいしか async-trait は必要ないはずです ( 個人的にはその場合はコメントつきで直接 -> Pin<Box<dyn Future...>> を書く方が人間にも AI にも意図がわかりやすくていいと思いますが… ) )

この記事の例の場合、ハンドラーのところで

pub async fn create_product(
    State(app_state): State<AppState>,

となっている ( generics がない ) ので、

struct AppState {
    product_use_case: ProductUseCase<Box<dyn ProductRepository>>,
}

の形を選んでいるはずで、だから ( Box<dyn ProductRepository> のところで ) 生の async fn in trait だとエラーになるという話なのは分かりますが、「必須」という書き方だとニュアンスが違うと思います

( 何か見落としていたり変なことを言っていたら教えてください )

カズマカズマ

コメントありがとうございます!
Cursorに感動するあまり書きなぐっていました。

勉強になります。
必須は言い過ぎだったかもしれません。
また色々と教えてください!!