Claude と考え、Devinで創る ー AI分業開発やってみた

こんにちは。GMOインターネット株式会社の長谷川です。
プロジェクト統括チームに所属しており、普段はプロジェクトマネジメント業務を担当し、プロジェクトの進捗管理や課題管理を行っています。元々はプログラマーを行っておりましたが、最近はプログラムに触れる機会が少なくなり、AIを用いた開発ってどんなんだろう、と思う機会が多くあります。
Devinを用いたバイブコーディングを試したこともありますが、簡単なツールは作れるが、完成度の高いものを残すには、設計部分で不安があるという課題を感じていました。

そこで浮かんだ仮説が、「設計をしっかりしたものから作ってもらえば、完成度の高いものになるのではないか?」というものです。この仮説を検証するため、ClaudeとDevinを組み合わせた開発を試してみました。
今回は、その結果をご紹介したいと思います。

記事の対象者

本記事は、PHPやLaravelフレームワークに少し知識がある方であれば理解できる内容となっております。また、過去にプログラミングを行っていただけれども、最近あまり触れていないなどの方も今後のコーディング手段としてご利用できる内容となっております。

注意点

本記事の情報は、著者の個人的な見解に基づくものであり、正確性などを保証するものではありません。

今回の実施内容

テキストファイルに記載されたURLリストを読み込み、各URLにアクセスしてコンテンツを取得・データベースに保存する試験的なプログラムです。Laravel上で3層アーキテクチャを採用し、非同期処理(Queue)を活用した実装を行いました。

実行環境

AIツール: Claude Sonnet4、Devin
OS: AlmaLinux 9.6 (Sage Margay)
PHP: 8.1.33
Framework: Laravel 10.49.0
Database: MySQL 8.0.43
Queue Driver: Database
Version Control: Git + GitHub

開発フロー

【設計フェーズ】Claudeで壁打ち&設計書作成(約2時間)

初期の指示内容

まず、Claudで壁打ちを実施し、クラス図を作成、そこまでの壁打ち内容とクラス図をインプットにREADME.mdを作成してもらいました。

クラス図の作成

壁打ちの結果を基に、Draw.ioでクラス図を作成しました。

主要なクラス構成:

  • Presentation Layer: RegisterDomainCommand
  • Domain Layer: DomainService, HttpClientService, エンティティ群
  • Infrastructure Layer: Domain Model, DomainRepositoryEloquent

README.md内容

【実装フェーズ】Devinでコーディング(約30分)

初回実装

README.mdをDevinに読み込ませて実装を依頼したところ、約3分で基本的な実装が完了しました。

発生した課題と対応

課題1: Callbackの実装不備

ファイル処理のCallback部分が正しく実装されていませんでした。

    public function processFileLines(string $filePath): ProcessResult
    {
        if (!file_exists($filePath)) {
            return new ProcessResult(0, 1, ['File not found: ' . $filePath]);
        }

        $lines = file($filePath, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
        $successCount = 0;
        $errorCount = 0;
        $errors = [];

        foreach ($lines as $line) {
            $line = trim($line);
            
            if (empty($line) || str_starts_with($line, '#')) {
                continue;
            }

            if ($this->isValidDomain($line)) {
                $this->dispatchProcessDomainCheck($line);
                $successCount++;
            } else {
                $errorCount++;
                $errors[] = "Invalid domain format: {$line}";
            }
        }

        return new ProcessResult($successCount, $errorCount, $errors);
    }

コールバック用のメソッドとして以下を定義していたため、こちらを利用してファイル読み込み処理をコールバック処理にするように指示しました。

- processFileLines($filePath: string, $callback: Closure($lineNumber: int, $line: string)): void

結果、思ったよりもスムーズにコードの生成を実施してくれました。

    public function processFileLines(string $file_path): ProcessResultEntity
    {
        if (!file_exists($file_path)) {
            return new ProcessResultEntity(false, 0, 1, ['File not found: ' . $file_path]);
        }

        $successCount = 0;
        $errorCount = 0;
        $errors = [];

        $this->__processFileLines($file_path, function(int $line_number, string $line) use (&$successCount, &$errorCount, &$errors) {
            $line = trim($line);
            
            if (empty($line) || str_starts_with($line, '#')) {
                return;
            }

            if ($this->isValidDomain($line)) {
                $this->dispatchProcessDomainCheck($line);
                $successCount++;
            } else {
                $errorCount++;
                $errors[] = "Invalid domain format: {$line}";
            }
        });

        return new ProcessResultEntity($errorCount === 0, $successCount, $errorCount, $errors);
    }

    private function __processFileLines(string $file_path, callable $callback): void
    {
        $handle = fopen($file_path, 'r');
        
        if ($handle === false) {
            return;
        }

        try {
            $line_number = 0;
            while (($line = fgets($handle)) !== false) {
                $line_number++;
                $line = rtrim($line, "\r\n");
                $callback($line_number, $line);
            }
        } finally {
            fclose($handle);
        }
    }

課題2: エラーハンドリング不足

URLにアクセスできない場合の処理が未実装でした。

対応: 以下の要件を追加指示

  • 接続失敗時はスキップして次のURLへ
  • エラー内容をログに記録
  • 504 Gateway Timeoutとして統一処理

完成

待ち時間含めて30分程度で、実装が完成しました。

工数比較

項目従来の方法Claude × Devin削減率
設計4時間2時間50%
実装4時間0.5時間88%
合計8時間2.5時間69%

通常であれば、8時間ぐらいかかるかな思いますが、今回のアプローチでは2.5時間で完了しました。

品質面での発見

良かった点:

  • 実装が高速: READMEが明確だったため、Devinの実装が迅速だった
  • コードの一貫性: アーキテクチャに沿った実装が保たれた

課題点:

  • 初回実装の細かい漏れ: CallbackやエラーハンドリングなどDevinの実装には細部の見直しが必要
  • README品質への依存: 設計書が不明瞭だと実装品質が下がる可能性

学びとポイント

Claudeの得意なこと

  • 設計の壁打ち相手: 曖昧な要件を対話で詰めていける
  • ドキュメント作成: READMEの作成が得意

Devinの得意なこと

  • 「どう作るか」の実行: 明確な設計書があれば高速に実装できる
  • 定型コードの量産: ボイラープレートコードの生成が速い
  • 基本的な実装: 標準的なパターンに沿った実装は安定している

分業のコツ

  • Claudeで設計を固める: 実装前に設計を丁寧に作成する
  • READMEを丁寧に書く: Devinへの指示書として機能する設計書を作る
  • 検証と修正を織り込む: Devinの初回実装は60点と想定し、レビューと修正を前提とする

実装の詳細

Repository Patternの採用

インフラ層への依存を抽象化するため、Repository Patternを採用しました。当たり前かもしれませんが、Providersへの登録も行ってくれています。

// ドメイン層にインターフェースを定義
interface DomainRepository
{
    public function findByUrl(string $url): ?DomainEntity;
    public function create(DomainEntity $domain): void;
    public function update(DomainEntity $domain): void;
}

// インフラ層で実装
class DomainRepositoryEloquent implements DomainRepository
{
    // Eloquentを使った実装
}

今後の展望

さらなる改善の余地

  • テストコードの生成: 設計書からテストケースも生成できるか検証
  • エラーハンドリング処理: エラーハンドリングの自動実装
  • より複雑な要件での検証: マイクロサービスなど複雑なアーキテクチャでの適用

まとめ

Claudeで設計、Devinで実装で比較的短時間で飽きることなく開発できました。
まだ、試験的な利用しないツールの開発という意味のわからない実装をしてしまったため、次回は同じアプローチで実際に利用するツール開発を行いたいと思います。

プロジェクトマネージャーとして10年ぶりに本格的なコーディングに取り組んだ結果、AIとの協働によって、設計力を活かしながら実装速度を大幅に向上させるという新しい働き方の可能性を感じました。

みなさんも、ぜひこのアプローチを試してみてください。


次回も機会があれば、生成AIの活用方法について紹介していきますので、お楽しみにしてください。

  • README内容
# ドメインチェックプロジェクト設計書

## プロジェクト概要

AlmaLinux 9.6 環境上で Laravel を使用したドメインチェック機能の実装。
TXTファイルからドメインリストを読み込み、HTTP接続確認を非同期で実行する。

---

## 技術スタック

### 基盤環境
- **OS**: AlmaLinux 9.6 (Sage Margay)
- **PHP**: 8.1.33
- **Framework**: Laravel 10.49.0
- **Database**: MySQL 8.0.43
- **Queue Driver**: Database
- **Version Control**: Git + GitHub

### 外部ライブラリ
- **HTTP Client**: Guzzle HTTP 7.x
- **ORM**: Eloquent (Laravel標準)

---

## アーキテクチャ設計

### パッケージ構成
```
App/
├── Console/Commands/           # Artisanコマンド
│   └── RegisterDomainCommand
├── Domain/Monitoring/          # ドメイン監視ドメイン層
│   ├── Entities/
│   │   ├── DomainEntity        # ドメインエンティティ
│   │   └── ProcessResultEntity # 処理結果エンティティ
│   ├── ValueObjects/           # 値オブジェクト
│   │   └── HttpResponse
│   ├── Services/               # ドメインサービス
│   │   ├── HttpClientService
│   │   └── DomainService
│   └── Repositories/           # データアクセス抽象化
│       ├── DomainRepository (interface)
│       └── TransactionManager (interface)
├── Infrastructure/Monitoring/  # インフラストラクチャ層
│   ├── Models/                 # Eloquentモデル
│   │   └── Domain
│   └── Repositories/           # リポジトリ実装
│       ├── DomainRepositoryEloquent
│       └── TransactionManagerEloquent  # トランザクション管理実装
└── Jobs/                       # 非同期処理
    └── ProcessDomainCheckJob
```

### 主要クラス

#### 1. RegisterDomainCommand
**責任**: TXTファイルからドメインリストを読み込み、ドメインチェックジョブを実行
- **名前空間**: `App\Console\Commands\`
- **主要メソッド**:
  - `handle(): int` - コマンド実行メイン処理
  - `validateFilePath($file_path: string): bool` - ファイルパス検証
  - `displaySuccessResult($result: ProcessResultEntity): void` - 成功結果表示
  - `displayErrorResult($result: ProcessResultEntity): void` - エラー結果表示

#### 2. DomainService
**責任**: ドメインチェックのビジネスロジック統括
- **名前空間**: `App\Domain\Monitoring\Services\`
- **依存関係**: HttpClientService, DomainRepository, TransactionManager
- **主要メソッド**:
  - `processFileLines($file_path: string): ProcessResultEntity` - ファイル処理
  - `checkDomain($url: string): bool` - ドメインチェック実行
  - `__processFileLines($file_path: string, $callback: Closure($line_number: int, $line: string)): void` - ファイル行処理
  - `dispatchProcessDomainCheck($url: string): void` - ジョブディスパッチ

#### 3. HttpClientService
**責任**: HTTP通信の抽象化
- **名前空間**: `App\Domain\Monitoring\Services\`
- **依存関係**: Guzzle HTTP Client
- **主要メソッド**:
  - `__construct($config: array = []): void` - 設定付きコンストラクタ
  - `get($url: string, $config: array = []): HttpResponse` - HTTP GET リクエスト

#### 4. DomainEntity
**責任**: ドメイン情報のデータ構造(ドメイン層エンティティ)
- **名前空間**: `App\Domain\Monitoring\Entities\`
- **属性**:
  - `$url: string` - ドメインURL
  - `$name?: string` - 名前
  - `$status_code?: int` - HTTPステータスコード
  - `$contents?: string` - コンテンツ情報
  - `$body?: string` - レスポンスボディ

#### 5. ProcessResultEntity
**責任**: ドメインサービスの処理結果(ドメイン層エンティティ)
- **名前空間**: `App\Domain\Monitoring\Entities\`
- **属性**:
  - `$success: bool` - 処理結果
  - `$success_count: int` - 成功件数
  - `$error_count: int` - 失敗件数
  - `$errors: array = []` - 失敗内容

#### 6. ProcessDomainCheckJob
**責任**: 非同期ドメインチェック処理
- **名前空間**: `App\Jobs\`
- **属性**:
  - `$url: string` - チェック対象URL

#### 7. DomainRepository (Interface + Implementation)
**責任**: ドメインデータの永続化
- **インターフェース**: `App\Domain\Monitoring\Repositories\DomainRepository`
- **実装**: `App\Infrastructure\Monitoring\Repositories\DomainRepositoryEloquent`

#### 8. Domain (Eloquent Model)
**責任**: データベースとのORM マッピング
- **名前空間**: `App\Infrastructure\Monitoring\Models\`
- **継承**: `Illuminate\Database\Eloquent\Model`

#### 9. HttpResponse
**責任**: HTTP レスポンス情報の値オブジェクト
- **名前空間**: `App\Domain\Monitoring\ValueObjects\`
- **属性**:
  - `$status_code: int` - HTTPステータスコード
  - `$body: string` - レスポンスボディ

#### 10. TransactionManager (Interface + Implementation)
**責任**: データベーストランザクションの管理と抽象化
- **インターフェース**: `App\Domain\Monitoring\Repositories\TransactionManager`
- **実装**: `App\Infrastructure\Monitoring\Repositories\TransactionManagerEloquent`
- **主要メソッド**:
  - `transaction(callable $callback): mixed` - トランザクション内で処理を実行
  - `begin(): void` - トランザクション開始
  - `commit(): void` - トランザクションコミット
  - `rollback(): void` - トランザクションロールバック

**配置の理由**:
- インターフェースをDomain層のRepositoriesに配置(DomainRepositoryと同じパターン)
- 実装をInfrastructure層のRepositoriesに配置(DomainRepositoryEloquentと同じパターン)
- テスタビリティの向上(モック可能)

---

## 機能仕様

### 1. ドメインチェック機能
- **入力**: TXTファイル(ドメインリスト)
- **処理**: HTTP/HTTPS接続確認
- **出力**: データベースへ結果保存
- **トランザクション**: 複数ドメインの一括登録時に一貫性を保証

### 2. ファイル処理仕様
**対応形式**: テキストファイル (.txt)
```txt
example.com
google.com
github.com
# これはコメント(#で始まる行はスキップ)
https://www.microsoft.com
http://stackoverflow.com
facebook.com
https://qiita.com/tags/laravel
amazon.co.jp
https://docs.laravel.com/10.x
invalid-domain-test

twitter.com
https://claude.ai
http://example.org/path/to/page
```

**処理ルール**:
- 改行文字・空白の自動削除
- 空行をスキップ
- コメント行(# 開始)をスキップ
- ドメイン形式の簡易バリデーション

### 3. HTTP接続チェック仕様
**プロトコル**: HTTPS → HTTP の順で試行
```php
$urls = [
    "https://{$domain}",
    "http://{$domain}"
];
```

**タイムアウト設定**:
- **接続タイムアウト**: 5秒
- **レスポンスタイムアウト**: 10秒

**成功判定**: ステータスコード 2xx, 3xx
**エラー処理**: 504 Gateway Timeout として統一処理

### 4. 非同期処理
- **Queue Driver**: Database
- **Job**: ProcessDomainCheckJob
- **再試行**: エラー時60秒後に再実行
- **結果保存**: 成功・失敗・エラーをデータベースに記録

---

## データベース設計

### テーブル構成
```sql
-- ドメイン情報
CREATE TABLE domains (
    id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    url VARCHAR(255) NOT NULL,
    name VARCHAR(255),
    status_code INT,
    contents TEXT,
    body TEXT,
    created_at TIMESTAMP,
    updated_at TIMESTAMP,
    INDEX idx_url (url)
);

-- ジョブキュー(Laravel標準)
CREATE TABLE jobs (
    id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    queue VARCHAR(255) NOT NULL,
    payload LONGTEXT NOT NULL,
    attempts TINYINT UNSIGNED NOT NULL,
    reserved_at TIMESTAMP NULL,
    available_at TIMESTAMP NOT NULL,
    created_at TIMESTAMP NOT NULL,
    INDEX jobs_queue_index (queue)
);
```

### データベース認証情報
```env
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=domain_check
DB_USERNAME=u_domain_check
DB_PASSWORD=DomainCheck2025!
```

---

## 実装順序

### Phase 1: 基盤構築
1. **データベース・マイグレーション**
   ```bash
   php artisan make:migration create_domains_table
   php artisan migrate
   ```

2. **基本モデル作成**
   ```bash
   php artisan make:model Domain
   ```

### Phase 2: インフラストラクチャ層
3. **TransactionManager実装**
   - インターフェース定義: `App\Domain\Monitoring\Repositories\TransactionManager`
   - Laravel実装: `App\Infrastructure\Monitoring\Repositories\TransactionManagerEloquent`
   - ServiceProviderへの登録

### Phase 3: ドメイン層
4. **ValueObjects実装**
   - HttpResponse値オブジェクト: `App\Domain\Monitoring\ValueObjects\HttpResponse`

5. **HttpClientService実装**
   - 名前空間: `App\Domain\Monitoring\Services\HttpClientService`
   - Guzzle HTTP設定
   - エラーハンドリング実装

6. **DomainRepository実装**
   - インターフェース定義: `App\Domain\Monitoring\Repositories\DomainRepository`
   - Eloquent実装: `App\Infrastructure\Monitoring\Repositories\DomainRepositoryEloquent`

### Phase 4: ビジネスロジック
7. **DomainService実装**
   - 名前空間: `App\Domain\Monitoring\Services\DomainService`
   - ファイル処理ロジック
   - ドメインチェック処理
   - TransactionManagerを利用したトランザクション管理

8. **ProcessDomainCheckJob実装**
   ```bash
   php artisan make:job ProcessDomainCheckJob
   ```

### Phase 5: UI・コマンド
9. **RegisterDomainCommand実装**
   ```bash
   php artisan make:command RegisterDomainCommand
   ```

10. **動作確認・テスト**

---

## 運用・保守

### コマンド実行例
```bash
# ドメインチェック実行
php artisan domain:register /path/to/domains.txt

# キューワーカー起動
php artisan queue:work

# ジョブ状況確認
php artisan queue:monitor
```

### ログ・監視
- **アプリケーションログ**: `storage/logs/laravel.log`
- **ジョブ失敗ログ**: `failed_jobs` テーブル
- **処理進捗**: コマンド出力+データベース状態

### セキュリティ考慮事項
- SSL証明書検証の適切な設定
- レート制限によるサーバー負荷軽減
- Personal Access Tokenの安全な管理
- データベース認証情報の適切な保護

---

## 今後の拡張可能性

### 機能拡張
- **Webインターフェース**: ファイルアップロード+結果表示画面
- **API化**: RESTful API提供
- **レポート機能**: CSV・PDF出力
- **スケジュール実行**: 定期的なドメインチェック

### 技術改善
- **Redis Queue**: 高パフォーマンス化
- **並列処理**: マルチプロセス対応
- **WHOIS連携**: より詳細なドメイン情報取得
- **Docker化**: 環境構築の標準化

---

## 参考資料

- [Laravel Setup Guide](laravel-setup-guide.md) - 環境構築手順
- [Laravel Documentation](https://laravel.com/docs) - 公式ドキュメント
- [Guzzle HTTP Documentation](https://docs.guzzlephp.org/) - HTTP Client仕様

---

# Laravel Domain Check プロジェクト クラス設計

## 📁 パッケージ構成

### App\Console\Commands
```php
class RegisterDomainCommand extends Command
{
    - string $signature
    - string $description
    - DomainService $domainService
    
    + handle(): int
    + validateFilePath($file_path: string): bool
    + displaySuccessResult($result: ProcessResult): void
    + displayErrorResult($result: ProcessResult): void
}
```

### App\Domain\Monitoring\Services

#### DomainService
```php
class DomainService
{
    - HttpClientService $http_client_service
    - DomainRepository $domain_repository
    - TransactionManager $transaction_manager
    
    + processFileLines($file_path: string): ProcessResult
    - checkDomain($url: string): bool
    - __processFileLines($file_path: string, $callback: Closure($line_number: int, $line: string)): void
    - dispatchProcessDomainCheck($url: string): void
}
```

#### HttpClientService
```php
class HttpClientService
{
    - Client $client
    
    + __construct($config: array = []): void
    + get($url: string, $config: array = []): HttpResponse
}
```

### App\Domain\Monitoring\Entities

#### DomainEntity
```php
class DomainEntity
{
    - string $url
    - string $name
    - int $status_code
    - string $contents
    - string $body
    
    + __construct($url: string, $name?: string, $status_code?: int, $contents?: string, $body?: string): void
    + getUrl(): string
    + getName(): ?string
    + getStatusCode(): ?int
    + getContents(): ?string
    + getBody(): ?string
}
```

#### ProcessResult
```php
class ProcessResult
{
    - bool $success
    - int $success_count
    - int $errorCount
    - array $errors
    
    + __construct($success: bool, $success_count: int, $error_count: int, $errros: array = []): void
    + isSuccess(): bool
    + getSuccessCount(): int
    + getErrorCount(): int
    + getErrors(): array = []
}
```

### App\Domain\Monitoring\ValueObjects

#### HttpResponse
```php
class HttpResponse
{
    - int $status_code
    - string $body

    + __construct($status_code: int, $body?: string): void
    + getStatusCode(): int
    + getBody(): ?string
}
```

### App\Domain\Monitoring\Repositories

#### DomainRepository (Interface)
```php
interface DomainRepository
{
    + findByUrl($url: string): ?DomainEntity
    + create(DomainEntity): void
    + update(DomainEntity): void
    + delete(DomainEntity): void
}
```

#### TransactionManager (Interface)
```php
interface TransactionManager
{
    + transaction(callable $callback): mixed
    + begin(): void
    + commit(): void
    + rollback(): void
}
```

### App\Infrastructure\Monitoring\Models

#### Domain (Eloquent Model)
```php
class Domain extends Model
{
    + int $id
    + string $url
    + string $name
    + int $status_code
    + string $contents
    + string $body
    + timestamps
    
    + array $fillable
    + array $casts
}
```

### App\Infrastructure\Monitoring\Repositories

#### DomainRepositoryEloquent
```php
class DomainRepositoryEloquent implements DomainRepository
{
    - Domain $model
    
    + findByUrl($url: string): ?DomainEntity
    + create($domain_entity: DomainEntity): void
    + update($domain_entity: DomainEntity): void
    + delete($domain_entity: DomainEntity): void

    - modelToEntity($domain: Domain): DomainEntity
    - entityToModel($domain_entity: DomainEntity): Domain
}
```

#### TransactionManagerEloquent
```php
class TransactionManager implements \App\Domain\Monitoring\Repositories\TransactionManager
{
    + transaction(callable $callback): mixed
    + begin(): void
    + commit(): void
    + rollback(): void
}
```

### App\Jobs

#### ProcessDomainCheckJob
```php
class ProcessDomainCheckJob implements ShouldQueue
{
    + string $url
    
    + __construct($url: string): void
    + handle($domain_service: DomainService): void
}
```

---

## 🔗 クラス関係図

### 依存関係
- `RegisterDomainCommand` → `DomainService`
- `DomainService` → `HttpClientService`
- `DomainService` → `DomainRepository`
- `DomainService` → `TransactionManager`
- `DomainService` → `ProcessDomainCheckJob`
- `HttpClientService` → `HttpResponse`
- `DomainRepositoryEloquent` → `Domain` (Eloquent Model)
- `DomainRepositoryEloquent` → `DomainEntity`
- `ProcessDomainCheckJob` → `DomainService`
- `TransactionManagerEloquent` → `DB` (Laravel Facade)

### 実装関係
- `DomainRepositoryEloquent` implements `DomainRepository`
- `TransactionManagerEloquent)` implements `TransactionManager`

### 組成関係
- `DomainService` creates `ProcessResult`
- `HttpClientService` creates `HttpResponse`
- `DomainRepositoryEloquent` converts between `Domain` ↔ `DomainEntity`

---

## 🏗️ アーキテクチャパターン

### レイヤーアーキテクチャ
1. **Presentation Layer**: Console Commands
2. **Application Layer**: Jobs (非同期処理)
3. **Domain Layer**: Services, Entities, Value Objects, Repository Interfaces
4. **Infrastructure Layer**: Models, Repository Implementations

### ドメイン駆動設計 (DDD) 要素
- **Entity**: `DomainEntity`
- **Value Object**: `HttpResponse`
- **Service**: `DomainService`, `HttpClientService`
- **Repository Pattern**: `DomainRepository` interface + `DomainRepositoryEloquent` implementation
- **Infrastructure Abstraction**: `TransactionManager` interface + `TransactionManagerEloquent` implementation

### 依存性注入 (DI)
- Service Provider によるDI設定
- インターフェースによる抽象化
- テスタビリティの向上

### トランザクション管理
- **責任分離**: Domain層がトランザクションインターフェースを定義
- **抽象化**: Domain層はLaravel固有の実装に依存しない
- **実装**: Infrastructure層でLaravel DBファサードを使用
- **テスタビリティ**: モック可能なインターフェース設計
- **使用例**:
```php
// DomainServiceでの使用例
public function processMultipleDomains(array $domains): ProcessResult
{
    return $this->transaction_manager->transaction(function () use ($domains) {
        foreach ($domains as $domain) {
            $this->domain_repository->create($domain);
        }
        return new ProcessResult(true, count($domains), 0);
    });
}
```

---

## 🔧 ServiceProvider設定

### AppServiceProvider
```php
public function register(): void
{
    // TransactionManagerのバインディング
    $this->app->bind(
        \App\Domain\Monitoring\Repositories\TransactionManager::class,
        \App\Infrastructure\Monitoring\Repositories\TransactionManagerEloquent::class
    );
    
    // DomainRepositoryのバインディング
    $this->app->bind(
        \App\Domain\Monitoring\Repositories\DomainRepository::class,
        \App\Infrastructure\Monitoring\Repositories\DomainRepositoryEloquent::class
    );
}
```

ブログの著者欄

長谷川 零

GMOインターネット株式会社

2015年1月にGMOインターネットグループ株式会社に入社。インフラチームに配属され、マネージャーを経て、現在、インフラ・運用本部プロジェクト統括チームに所属。 2025年GMOインターネット株式会社へ転籍。

採用情報

関連記事

KEYWORD

TAG

もっとタグを見る

採用情報

SNS FOLLOW

GMOインターネットグループのSNSをフォローして最新情報をチェック