Rust×Clean Archで今更入門するCursor爆速開発
要件定義から本格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
は言い過ぎではないでしょうか?
async_fn_in_trait
が stable に入った 1.75 以降は、async fn を含む trait をdyn
で使いたいときくらいしかasync-trait
は必要ないはずです ( 個人的にはその場合はコメントつきで直接-> Pin<Box<dyn Future...>>
を書く方が人間にも AI にも意図がわかりやすくていいと思いますが… ) )この記事の例の場合、ハンドラーのところで
となっている ( generics がない ) ので、
の形を選んでいるはずで、だから (
Box<dyn ProductRepository>
のところで ) 生の async fn in trait だとエラーになるという話なのは分かりますが、「必須」という書き方だとニュアンスが違うと思います( 何か見落としていたり変なことを言っていたら教えてください )
コメントありがとうございます!
Cursorに感動するあまり書きなぐっていました。
勉強になります。
必須は言い過ぎだったかもしれません。
また色々と教えてください!!