コンテンツにスキップ

アプリケーション構造

概要

EC-SPOKEシステムの具体的なディレクトリ構成と、各種コンポーネントの配置方針を定義します。実装時の具体的なガイドラインとして参照してください。

基本方針

EC-SPOKEシステムのアプリケーション構造は、以下の原則に基づいて設計されています:

1. ドメイン駆動の構造化

  • モデルとドメインの分離: Eloquentモデル(app/Models/{Domain})とドメイン固有の振る舞い(app/Domains/{Domain})を明確に分離し、責務を明確化する
  • ドメイン別の集約: 各ドメイン(Catalog、Order、Page等)は、モデル・サービス・ポリシー・アクション・オブザーバを独立したディレクトリに集約する
  • グローバルコンポーネントの明確化: 複数ドメインで共通利用されるコンポーネント(Enums、Rules、Services、Workflows)は、グローバルに配置し、ドメイン固有のものと区別する

2. レイヤー別の責務分離

  • プレゼンテーション層: Admin(Filament管理画面)、Frontend(Livewireコンポーネント)、Http(API/Webコントローラ)を明確に分離
  • ビジネスロジック層: ドメインサービス、ワークフロー、アクションを配置し、UIから独立したビジネスロジックを実現
  • インフラ層: Providers、Support、Traits、Facades、Consoleコマンドを配置し、アプリケーション基盤を提供

3. 近接配置の原則

  • Filamentリソースとビュー: 近接配置ルールに従い、リソースとビューを同じディレクトリ配下に配置し、探索性を向上
  • ワークフロー関連ファイル: ワークフロー名別のディレクトリに、Workflow、Pipeline、Context、State、Actions、Jobs、Pipesを集約

4. イベント駆動アーキテクチャ

  • Observers: モデルのライフサイクルに密接に関連する処理(検索インデックス更新、監査ログ等)
  • Events/Listeners: ビジネスイベントを表現し、ドメイン間の疎結合な連携を実現
  • 使い分け: モデルの状態変化はObservers、ビジネス的に意味のある出来事はEvents/Listenersを使用

5. 技術スタックの活用

  • PHP 8.3以上: readonly class、enum、match式、型付きプロパティなど最新の機能を積極的に活用
  • Laravel標準構造: プロジェクトルート配下のディレクトリ(database、routes、resources、bootstrap、tests、storage、public)はLaravel標準に従う
  • Filament v4: 管理画面のリソース、コンポーネント、ウィジェットをapp/Admin/配下に配置

ディレクトリ構成例

app/
├── Models/
│   ├── Catalog/
│   │   ├── Product.php
│   │   ├── ProductVariant.php
│   │   ├── Inventory.php
│   │   └── Price.php
│   ├── Order/
│   │   ├── Order.php
│   │   └── OrderItem.php
│   └── ...(他ドメインも同様に配置)
├── Domains/
│   ├── Catalog/
│   │   ├── Services/
│   │   │   ├── ProductCatalogService.php
│   │   │   └── InventoryService.php
│   │   ├── Policies/
│   │   │   └── ProductPolicy.php
│   │   ├── Actions/
│   │   │   └── ArchiveProductAction.php
│   │   ├── Observers/
│   │   │   └── ProductObserver.php
│   │   ├── ValueObjects/
│   │   │   └── Price.php
│   │   └── Constants/
│   │       └── ProductConstants.php
│   └── Order/
│       ├── Services/
│       │   └── OrderPlacementService.php
│       ├── ValueObjects/
│       │   ├── ReservedInventory.php
│       │   ├── PaymentResult.php
│       │   └── ShippingArrangement.php
│       └── ...

├── Enums/
│   ├── OrderStatus.php
│   ├── PaymentStatus.php
│   └── ...(グローバルなEnum)

├── Rules/
│   ├── SubscriptionPlanUniqueRule.php
│   ├── ValidProductVariantRule.php
│   └── ...(カスタムバリデーションルール)

├── Services/
│   ├── CartService.php
│   └── PageCompiler.php

├── Workflows/
│   └── OrderPlacement/
│       ├── OrderPlacementWorkflow.php
│       ├── OrderPlacementPipeline.php
│       ├── OrderPlacementContext.php
│       ├── OrderPlacementState.php
│       ├── Actions/
│       │   ├── ReserveInventoryAction.php
│       │   ├── ProcessPaymentAction.php
│       │   └── ArrangeShippingAction.php
│       ├── Jobs/
│       │   ├── ReserveInventoryJob.php
│       │   ├── ProcessPaymentJob.php
│       │   └── ArrangeShippingJob.php
│       └── Pipes/
│           ├── ReserveInventoryPipe.php
│           └── ProcessPaymentPipe.php

├── Admin/
│   ├── Catalog/
│   │   ├── Resources/
│   │   │   └── ProductResource.php
│   │   └── Views/
│   │       └── product-form.blade.php
│   └── ...

├── Frontend/
│   ├── Components/
│   ├── PageBuilder/
│   ├── Services/
│   └── Themes/
│       ├── Default/
│       └── Bootstrap/

├── Http/
│   ├── Controllers/
│   ├── Middleware/
│   ├── Requests/
│   └── Resources/
│       ├── ProductApiResource.php
│       ├── OrderApiResource.php
│       └── CategoryApiResource.php

├── Providers/
│   ├── AppServiceProvider.php
│   ├── Filament/
│   │   └── AdminPanelProvider.php
│   └── ThemeServiceProvider.php

├── Support/
│   ├── Helpers/
│   ├── helpers.php
│   └── MultibyteSlug.php

├── Traits/
│   └── HasSortableTree.php

├── Facades/
│   └── Cart.php

├── Console/
│   └── Commands/
│       ├── CleanupOrphanedBlocks.php
│       ├── CompilePageCommand.php
│       ├── ConvertEcSpokeLang.php
│       └── ExportEcSpokeLang.php

├── Observers/
│   └── ...(グローバルなオブザーバ)

├── Events/
│   └── .gitkeep

├── Listeners/
│   └── .gitkeep

├── Jobs/
│   └── .gitkeep

├── Mail/
│   └── .gitkeep

├── Exceptions/
│   └── .gitkeep

├── Broadcast/
│   └── .gitkeep

└── Notifications/
    └── TwoFactorEmailCodeNotification.php

config/
├── catalog.php
├── orders.php
├── subscriptions.php
├── payment.php
└── ...(ドメイン別設定ファイル)

database/
├── factories/
│   └── ...(モデルファクトリー)
├── migrations/
│   └── ...(データベースマイグレーション)
└── seeders/
    └── ...(データベースシーダー)

routes/
├── web.php
└── console.php

resources/
├── views/
│   └── ...(Bladeビュー)
├── lang/
│   └── ...(多言語翻訳ファイル)
└── css/
    └── ...(CSSファイル)

bootstrap/
├── app.php
└── providers.php

tests/
└── ...(PHPUnitテストファイル)

各ディレクトリの役割分担と配置ルール

各ディレクトリの役割分担と配置ルールをディレクトリ構成に従って整理します。後から見たときに「これこれはここに配置する」というルールが分かるように構成しています。

Models (app/Models/)

  • 役割: Eloquentモデルを配置。データベーステーブルと1対1対応するエンティティを定義
  • 配置ルール: ドメイン別にサブディレクトリを作成(例: app/Models/Catalog/, app/Models/Order/
  • : Product.php, Category.php, Order.php, User.php, Brand.php, Page.php, PageArea.php, PageBlock.php, OrderItem.php, Address.php, Admin.php

Domains (app/Domains/{Domain}/)

  • 役割: ドメイン固有の振る舞いを配置。ユースケース毎のロジックを横断的に再利用

サービス層

  • ユースケース単位のサービス: app/Domains/{Domain}/Services に配置
  • ユースケースごとにトランザクション境界・バリデーション・イベント発火を担当
  • Eloquent への直接的な依存はここに集める
  • PHP 8.3の型付きプロパティとコンストラクタプロパティプロモーションを活用

値オブジェクト

  • ドメインの概念を表現: app/Domains/{Domain}/ValueObjects に配置
  • ドメインの概念を表現する不変オブジェクト
  • readonly class として実装し、ビジネスルールを含む
  • ワークフロー固有のデータ構造(Context、State等)は app/Workflows/{WorkflowName}/ 配下に配置

ポリシー

  • 認可ロジック: app/Domains/{Domain}/Policies に配置
  • Filament/API 双方の認可に再利用
  • Filament の can* 系メソッドは自動的に Policy を参照

アクション

  • ビジネスアクション: app/Domains/{Domain}/Actions に配置
  • Filament Actions やジョブ/コマンドラインから呼び出せる単位にまとめる
  • UI ごとの差異を吸収

オブザーバ

  • モデル更新の副作用: app/Domains/{Domain}/Observers に配置
  • モデル更新の副作用(検索インデックス更新、監査ログ、イベント発火など)を一元管理

定数

  • ドメイン固有の定数: app/Domains/{Domain}/Constants に配置
  • 変更頻度が低く、ドメイン固有の定数値を管理
  • 値オブジェクトとして readonly class を活用する場合もある

Enums (app/Enums/)

  • 役割: グローバルな列挙型を配置。複数のドメインで共通利用される列挙型を定義
  • 配置ルール: ドメイン固有のEnumは app/Domains/{Domain}/Constants/ に配置。グローバルなもののみここに配置
  • : App\Enums\OrderStatus, App\Enums\PaymentStatus, App\Enums\Page\AreaType
  • 注意: PHP 8.3のEnum機能を活用。Backed Enum(値を持つEnum)やメソッド付きEnumを積極的に使用

Rules (app/Rules/)

  • 役割: カスタムバリデーションルールを配置。FormRequest とサービス層の両方で再利用可能なルールを定義
  • 配置ルール: グローバルに配置。ドメイン固有の複雑なバリデーションロジック(例: サブスク契約の重複チェック、商品バリエーションの整合性チェック)を一元管理
  • : App\Rules\SubscriptionPlanUniqueRule, App\Rules\ValidProductVariantRule

Services (app/Services/)

  • 役割: ドメイン横断的なアプリケーションサービスを配置。複数のドメインにまたがる処理や、アプリケーション全体で共通利用される機能を担当
  • 配置ルール: 特定のドメインに属さないサービスを配置。ドメイン固有のサービスは app/Domains/{Domain}/Services/ に配置
  • 使用例:
  • カート管理: セッション管理、商品追加・削除、合計計算など(CartService)。商品・注文・ユーザーなど複数ドメインにまたがる処理
  • その他: 複数ドメインにまたがる共通処理、アプリケーション全体で使われる機能
  • ドメイン固有のサービスとの違い:
  • app/Services/: カートなど、複数ドメインにまたがる処理
  • app/Domains/{Domain}/Services/: 商品カタログ管理、注文確定、ページコンパイルなど、特定ドメインのビジネスロジック
  • Serviceの使い分け:
  • 使用する場合: ビジネスロジックを持つクラス、状態を持つ可能性がある(セッション、キャッシュ、DB等)
  • 特徴: 複数のメソッドを持つ、依存注入で使用、テストしやすい(モック可能)
  • : CartService::addToCart()(セッションに状態を保存), PageCompiler::compile()(複雑な処理、ファイル操作)
  • 判断基準: 状態を持つか?複数のメソッドが必要か?複雑なビジネスロジックか?依存関係があるか?
  • : CartService.php(カート管理サービス)
  • 注意: PageCompilerは現在app/Services/に配置されているが、Pageドメイン固有の処理のため、将来的にはapp/Domains/Page/Services/への移動を検討

Workflows (app/Workflows/{WorkflowName}/)

  • 役割: 決済・在庫連携など複数手順を跨ぐ処理を、Laravel PipelineとWorkflowパターンを用いてパイプライン化し、各ステップをAction/ジョブに分離する
  • 使い分け:
  • Pipeline: 同期処理(数秒以内)。ユーザーリクエスト内で完結する処理(カート計算、価格計算、バリデーション等)
  • Workflow: 同期・非同期両対応(数分〜数時間)。外部API呼び出しが多い処理、進捗を追跡したい処理(注文確定、決済処理、配送手配、在庫連携等)
  • 配置ルール: ワークフロー名別にサブディレクトリを作成し、その中に以下のファイル・ディレクトリを配置
  • {WorkflowName}Workflow.php: ワークフローオーケストレーター(非同期処理用)。各ステップの実行順序と状態遷移を管理
  • {WorkflowName}Pipeline.php: パイプラインオーケストレーター(同期処理用、オプション)
  • {WorkflowName}Context.php: Pipeline用のコンテキスト。各Pipe間でデータを伝播
  • {WorkflowName}State.php: Workflow用の状態管理モデル。データベースに永続化
  • Actions/: ビジネスロジック。ワークフロー固有のActionを配置
  • Jobs/: 非同期処理。ワークフロー固有のジョブを配置
  • Pipes/: Pipeline用のPipe(同期処理用、オプション)
  • 注意: ワークフロー固有のデータ構造(Context、State等)はここに配置。ドメインの概念を表現する値オブジェクトは app/Domains/{Domain}/ValueObjects/ に配置
  • : OrderPlacement/OrderPlacementWorkflow.php, OrderPlacement/Actions/ReserveInventoryAction.php, OrderPlacement/Jobs/ProcessPaymentJob.php
  • 詳細: Workflow-Pipelineパターン を参照

Admin (app/Admin/)

  • 役割: Filament管理画面のリソース、コンポーネント、ウィジェット、ページ、プラグインを配置
  • 配置ルール:
  • Resources/: Filamentリソース(CRUD管理)。各リソースは {ResourceName}Resource.php として配置
  • Components/: Filamentカスタムコンポーネント。Livewireコンポーネントやカスタムフィールドを配置
  • Widgets/: Filamentウィジェット。ダッシュボード用の小型コンポーネント
  • Pages/: Filamentページ。独立したページコンポーネントを配置
  • Plugins/: Filamentプラグインの拡張。外部パッケージのプラグインを拡張・カスタマイズする場合に配置。各プラグインは {PluginName}/ ディレクトリに集約し、プラグインクラス、ページ、関連ロジックを同一ディレクトリに配置
  • Base/: 基底クラス・共通機能。複数のリソースで共通利用される基底クラス
  • Views/: ビューは近接配置ルールに従い、各コンポーネント・リソースと同じディレクトリ直下の Views/ に配置
  • プラグイン拡張の配置例:
  • app/Admin/Plugins/LogViewer/CustomLogViewerPlugin.php - FilamentLogViewerパッケージの拡張プラグイン(Filament\Contracts\Plugin を実装)
  • app/Admin/Plugins/LogViewer/CustomLogTable.php - プラグインが提供するPage
  • app/Admin/Plugins/LogViewer/CustomLog.php - プラグイン固有のロジック(ログデータ処理等)
  • 注意: リソース/ウィジェット/フォームビューは app/Admin/{Domain} 以下で近接配置し、必要なサービスやポリシーを use App\Domains\... で参照する。ドメインフォルダ内のアクションやサービスを呼び出すことで、管理画面・API・ジョブのロジックを統一できる。監査ログ・設定変更などは Administration ドメインのサービスを経由して Filament から操作し、変更履歴を残す。詳細は近接配置ルールを参照

Frontend (app/Frontend/)

  • 役割: フロントエンドのLivewireコンポーネント、ページビルダー、テーマ別ビューを配置
  • 配置ルール:
  • Components/: Livewireコンポーネント(ページ)。認証、カート、商品一覧・詳細、チェックアウト等のページコンポーネント
  • PageBuilder/: ページビルダー機能。動的ページレンダリング、エリア・ブロックコンポーネント
  • Services/: フロントエンド用サービス。ブロック登録管理、テーマサービス
  • Themes/: テーマ別ディレクトリ(Default/Bootstrap等)。テーマ固有のビュー、アセット、ヘルパーを配置

Http (app/Http/)

  • 役割: HTTP層のコントローラ、ミドルウェア、リクエストクラス、API Resourceを配置
  • 配置ルール: Laravel標準の構造に従う
  • Controllers/: コントローラクラス。基底コントローラは Controller.php
  • Middleware/: ミドルウェア(Laravel 12では bootstrap/app.php で登録)
  • Requests/: FormRequestクラス。バリデーションルールとカスタムエラーメッセージを定義
  • Resources/: API Resourceクラス。JSONレスポンスの形式定義(JsonResourceを継承)
  • API Resourceの命名規則:
  • 形式: {Model}ApiResource(サフィックス方式)
  • : ProductApiResource, OrderApiResource, CategoryApiResource
  • 配置場所: app/Http/Resources/{Model}ApiResource.php
  • Filament Resourceとの区別: Filament Resource(app/Admin/Resources/{Model}Resource.php)とは完全に独立。名前空間が異なるため、同じディレクトリ名でも問題ない
  • 詳細: API層の命名規則 を参照

Providers (app/Providers/)

  • 役割: サービスプロバイダを配置。アプリケーション起動時の設定、サービス登録を担当
  • 配置ルール:
  • AppServiceProvider.php: アプリケーションプロバイダ
  • Filament/AdminPanelProvider.php: Filament管理パネル設定
  • ThemeServiceProvider.php: テーマ切り替えプロバイダ
  • 注意: Laravel 12では bootstrap/providers.php に登録

Support (app/Support/)

  • 役割: サポートクラス・ヘルパー関数を配置。アプリケーション全体で利用されるユーティリティ
  • 配置ルール:
  • Helpers/: ヘルパー関数群。image.php(画像ヘルパー), theme.php(テーマヘルパー)
  • helpers.php: グローバルヘルパー
  • MultibyteSlug.php: マルチバイトSlug生成などのユーティリティクラス
  • ヘルパー関数の使い分け:
  • 使用する場合: 純粋関数(状態を持たない、単純な変換・フォーマット処理)
  • 特徴: 入力→出力の変換処理、グローバルスコープから呼び出し可能、単一の責務
  • : get_image_url()(画像URL生成), date_time_format()(日付フォーマット), theme_asset()(アセットURL生成)
  • 判断基準: 状態を持たないか?単純な変換・フォーマット処理か?複数のメソッドが必要か?(不要ならヘルパー)

Traits (app/Traits/)

  • 役割: 再利用可能なトレイトを配置。複数のクラスで共通利用される機能を定義
  • 配置ルール: グローバルに配置
  • : HasSortableTree.php(ソート可能ツリートレイト。カテゴリで使用)

Facades (app/Facades/)

  • 役割: ファサード定義を配置。サービスコンテナに登録されたサービスの静的インターフェースを提供
  • 配置ルール: グローバルに配置
  • Facadeの使い分け:
  • 使用する場合: BladeテンプレートやLivewireコンポーネントから直接呼ぶ必要がある場合のみ(最小限の使用)
  • 特徴: Serviceをラップするだけ、静的メソッドとして呼び出せる、Bladeテンプレートから直接呼び出し可能
  • : Cart::addToCart()(BladeテンプレートやLivewireから呼ぶ場合のみ)
  • 判断基準: BladeテンプレートやLivewireコンポーネントから直接呼ぶ必要があるか?Laravel標準のFacade(Cache、DB、Auth等)と同じような使い方か?
  • 推奨: それ以外はServiceを依存注入で使用する方が良い(コントローラやサービスでは依存注入を優先)
  • : Cart.php(カートファサード)

Console (app/Console/Commands/)

  • 役割: Artisanコマンドを配置。バックグラウンド処理、メンテナンス、データ移行用のコマンド
  • 配置ルール: app/Console/Commands/ に配置。Laravel 12では自動登録されるため手動登録不要
  • :
  • CleanupOrphanedBlocks.php(存在しないブロッククラスを持つページブロックをクリーンアップ)
  • CompilePageCommand.php(ページビルダーで作成したページをBladeファイルにコンパイル)
  • ConvertEcSpokeLang.php(外部翻訳ファイルをLaravel形式にインポート)
  • ExportEcSpokeLang.php(Laravel翻訳ファイルを外部形式にエクスポート)

Observers (app/Observers/)

  • 役割: グローバルなオブザーバを配置。ドメイン固有のオブザーバは app/Domains/{Domain}/Observers/ に配置
  • 配置ルール: グローバルに配置。複数のドメインにまたがるオブザーバを配置
  • 注意: ドメイン固有のオブザーバは app/Domains/{Domain}/Observers/ に配置することを推奨

Notifications (app/Notifications/)

  • 役割: 通知クラスを配置。メール、SMS等の通知を定義
  • 配置ルール: グローバルに配置
  • : TwoFactorEmailCodeNotification.php(2FAメール認証コード通知)
  • 注意: Mailableクラス(app/Mail/)とは異なる。通知はより高レベルな抽象化で、複数のチャネル(メール、SMS、データベース等)に対応可能

config/ (config/)

  • 役割: 設定ファイルを配置。アプリケーションの設定値を定義
  • 配置ルール: Laravel標準に従い、ドメイン別に分割(例: config/catalog.php, config/orders.php, config/subscriptions.php
  • 注意: アプリケーション内では config('catalog.xxx') 形式で参照。env() 関数は設定ファイル内のみで使用し、アプリケーション内では config() を使用
  • : app.php, auth.php, cache.php, database.php, filament.php, filesystems.php, livewire.php, logging.php, mail.php, queue.php, services.php, session.php, theme.php

プロジェクトルート配下のディレクトリ

database/ (database/)

  • 役割: データベース関連ファイルを配置。マイグレーション、シーダー、ファクトリーを定義
  • 配置ルール: Laravel標準の構造に従う
  • factories/: モデルファクトリー。テストデータ生成用
  • migrations/: データベースマイグレーション。スキーマ変更を定義
  • seeders/: データベースシーダー。初期データ投入用
  • 注意: マイグレーションは時系列で管理され、YYYY_MM_DD_HHMMSS_create_table_name.php 形式で命名

routes/ (routes/)

  • 役割: ルート定義ファイルを配置。HTTPリクエストのルーティングを定義
  • 配置ルール: Laravel標準の構造に従う
  • web.php: Webルート(セッション、CSRF保護、Cookie暗号化が有効)
  • console.php: Artisanコマンドのスケジュール定義
  • 注意: APIルートは routes/api.php に配置(現在は未使用)。Laravel 12では bootstrap/app.php でルートファイルを登録

resources/ (resources/)

  • 役割: 未コンパイルのアセット(ビュー、言語ファイル、CSS等)を配置
  • 配置ルール: Laravel標準の構造に従う
  • views/: Bladeビューテンプレート。app/Admin/app/Frontend/ 配下のビューは近接配置ルールに従う
  • lang/: 多言語翻訳ファイル。{locale}/{group}.php 形式で配置
  • css/: CSSファイル。Tailwind CSSの設定ファイル等
  • 注意: app/Admin/ 配下のビューは近接配置ルールに従い、app/Admin/.../Views/ に配置。resources/views/ は標準的なLaravelビュー用

bootstrap/ (bootstrap/)

  • 役割: アプリケーション起動時の設定ファイルを配置
  • 配置ルール: Laravel標準の構造に従う
  • app.php: アプリケーション起動設定。ミドルウェア、例外処理、ルートファイルの登録
  • providers.php: サービスプロバイダの登録
  • cache/: 設定キャッシュ、ルートキャッシュ等(自動生成)
  • 注意: Laravel 12では app.php でミドルウェアやルートを登録。providers.php でサービスプロバイダを登録

tests/ (tests/)

  • 役割: PHPUnitテストファイルを配置。アプリケーションのテストを定義
  • 配置ルール: Laravel標準の構造に従う
  • Feature/: フィーチャーテスト。HTTPリクエスト、データベース操作を含む統合テスト
  • Unit/: ユニットテスト。単一のクラスやメソッドのテスト
  • 注意: テストは php artisan test で実行。RefreshDatabase トレイトを使用する場合は、テスト専用データベースを使用すること

storage/ (storage/)

  • 役割: アプリケーションが生成するファイルを配置。ログ、キャッシュ、アップロードファイル等
  • 配置ルール: Laravel標準の構造に従う
  • app/: アプリケーションが生成するファイル(公開されない)
  • framework/: フレームワークが生成するファイル(キャッシュ、セッション等)
  • logs/: ログファイル
  • 注意: .gitignore に含まれているため、バージョン管理されない

public/ (public/)

  • 役割: 公開ファイルを配置。Webサーバーのドキュメントルート
  • 配置ルール: Laravel標準の構造に従う
  • index.php: アプリケーションのエントリーポイント
  • assets/: コンパイル済みアセット(CSS、JavaScript、画像等)
  • 注意: Viteでコンパイルされたアセットはここに配置される

Observers (app/Observers/)

  • 役割: グローバルなオブザーバを配置。ドメイン固有のオブザーバは app/Domains/{Domain}/Observers/ に配置
  • 配置ルール: グローバルに配置。複数のドメインにまたがるオブザーバを配置
  • 注意: ドメイン固有のオブザーバは app/Domains/{Domain}/Observers/ に配置することを推奨

Observersとは: - モデルのライフサイクルイベント(created, updated, deleted等)に自動的に反応する仕組み - モデル保存時に自動的に実行される副作用処理を定義 - 例: 商品更新時に検索インデックスを更新、注文作成時に監査ログを記録

使用場面: - モデル保存時の自動的な副作用処理が必要な場合 - 複数の場所で同じ副作用処理を実行する必要がある場合 - モデルのライフサイクルに密接に関連する処理

実装例:

// app/Domains/Catalog/Observers/ProductObserver.php
class ProductObserver
{
    public function updated(Product $product): void
    {
        // 商品更新時に検索インデックスを更新
        UpdateProductIndexJob::dispatch($product);
    }
}

Events (app/Events/)

  • 役割: イベントクラスを配置。ドメインイベントやアプリケーションイベントを定義
  • 配置ルール: グローバルに配置。ドメイン固有のイベントは app/Domains/{Domain}/Events/ に配置することも検討
  • 使用例: OrderPlaced, ProductUpdated, UserRegistered など
  • 使用場面: ドメイン間の疎結合な連携、複数の非同期処理をトリガーする場面
  • 詳細: イベント駆動アーキテクチャの実装時に使用。event() 関数または Event::dispatch() で発火

Eventsとは: - ビジネスイベントを表現するクラス(例: OrderPlaced, ProductPublished) - モデルのライフサイクルではなく、ビジネス的に意味のある出来事を表現 - 複数のドメインにまたがる処理を疎結合に連携するために使用

使用場面: - 注文確定時に、在庫引当・決済処理・メール送信・監査ログなど複数の処理を実行したい場合 - ドメイン間の連携を疎結合にしたい場合 - 非同期処理をトリガーしたい場合

実装例:

// app/Events/OrderPlaced.php
class OrderPlaced
{
    public function __construct(
        public Order $order
    ) {}
}

// app/Domains/Order/Services/OrderPlacementService.php
public function placeOrder(Order $order): void
{
    DB::transaction(function () use ($order) {
        $order->status = OrderStatus::Placed;
        $order->save();

        // イベント発火
        event(new OrderPlaced($order));
    });
}

Listeners (app/Listeners/)

  • 役割: イベントリスナーを配置。イベント発生時に実行される処理を定義
  • 配置ルール: グローバルに配置。ドメイン固有のリスナーは app/Domains/{Domain}/Listeners/ に配置することも検討
  • 使用例: SendOrderConfirmationEmail, UpdateSearchIndex, LogAuditTrail など
  • 使用場面: イベント発生時の副作用処理(メール送信、検索インデックス更新、監査ログ等)
  • 非同期処理: ShouldQueue インターフェースを実装することで、リスナーを非同期で実行可能

Listenersとは: - イベントが発生したときに実行される処理を定義するクラス - 1つのイベントに対して複数のリスナーを登録可能 - イベント発火元のコードを変更せずに、新しい処理を追加できる

使用場面: - 注文確定時にメール送信、検索インデックス更新、監査ログ記録など複数の処理を実行したい場合 - イベント発火元のコードを変更せずに新しい処理を追加したい場合 - 非同期処理を実行したい場合

実装例:

// app/Listeners/SendOrderConfirmationEmail.php
class SendOrderConfirmationEmail implements ShouldQueue
{
    public function handle(OrderPlaced $event): void
    {
        // 非同期でメール送信
        Mail::to($event->order->customer->email)
            ->send(new OrderConfirmationMail($event->order));
    }
}

// app/Listeners/UpdateSearchIndex.php
class UpdateSearchIndex implements ShouldQueue
{
    public function handle(OrderPlaced $event): void
    {
        // 非同期で検索インデックス更新
        UpdateSearchIndexJob::dispatch($event->order);
    }
}

Observers、Events、Listenersの使い分け

補記:使い分けの比較表

観点 Observers Events/Listeners 直接呼び出し
トリガー モデルのライフサイクル(created, updated, deleted等) ビジネスイベント(OrderPlaced, ProductPublished等) メソッド呼び出し
焦点 モデルの状態変化 ビジネス的に意味のある出来事 処理の実行
結合度 モデルに密結合 疎結合(イベント名で連携) 密結合(直接依存)
実行タイミング モデル保存時に自動実行 明示的にイベント発火 メソッド呼び出し時
複数処理の実行 1つのObserverで複数処理 1つのイベントに複数のListener 複数メソッドを順次呼び出し
非同期処理 可能(ジョブをディスパッチ) 可能(ShouldQueueを実装) 可能(ジョブをディスパッチ)
拡張性 低い(Observerを変更する必要がある) 高い(新しいListenerを追加するだけ) 低い(呼び出し元を変更する必要がある)
使用例 商品更新時に検索インデックス更新 注文確定時にメール送信・監査ログ・在庫更新 カートに商品を追加
推奨場面 モデルのライフサイクルに密接に関連する処理 ドメイン間の連携、複数の処理を実行したい場合 単純な処理、密結合で問題ない場合

使い分けの判断フローチャート

1. モデルのライフサイクル(created/updated/deleted)に密接に関連する処理?
   → Yes → Observers
   ↓ No
2. ビジネス的に意味のある出来事(注文確定、商品公開等)を表現したい?
   → Yes → Events/Listeners
   ↓ No
3. 単純な処理で、密結合で問題ない?
   → Yes → 直接呼び出し
   ↓ No
   → Events/Listeners(疎結合を優先)

実践的なルール

Observersを使う場合

  • ✅ モデル保存時に必ず実行したい処理(検索インデックス更新、監査ログ等)
  • ✅ モデルのライフサイクルに密接に関連する処理
  • ⚠️ ビジネスロジックが複雑な場合は、Events/Listenersを検討

Events/Listenersを使う場合

  • ✅ ドメイン間の連携を疎結合にしたい場合
  • ✅ 1つのイベントに対して複数の処理を実行したい場合
  • ✅ イベント発火元のコードを変更せずに新しい処理を追加したい場合
  • ✅ 非同期処理を実行したい場合

直接呼び出しを使う場合

  • ✅ 単純な処理で、密結合で問題ない場合
  • ✅ カートに商品を追加、価格計算など、即座に結果が必要な処理
  • ⚠️ 複数の処理を実行する場合は、Events/Listenersを検討

Jobs (app/Jobs/)

  • 役割: グローバルなキュージョブを配置。ワークフロー固有のジョブは app/Workflows/{WorkflowName}/Jobs/ に配置
  • 配置ルール: グローバルに配置。複数のワークフローで共通利用されるジョブを配置
  • 注意: ワークフロー固有のジョブは app/Workflows/{WorkflowName}/Jobs/ に配置することを推奨
  • 使用例: メール送信、検索インデックス更新、外部API連携など

Mail (app/Mail/)

  • 役割: Mailableクラスを配置。メール送信用のクラスを定義
  • 配置ルール: グローバルに配置。ドメイン固有のMailableは app/Domains/{Domain}/Mail/ に配置することも検討
  • 注意: Notifications(app/Notifications/)とは異なる。Mailableはメール送信専用、Notificationsは複数チャネル対応
  • 使用例: OrderConfirmationMail, PasswordResetMail, WelcomeMail など

Exceptions (app/Exceptions/)

  • 役割: カスタム例外クラスを配置。アプリケーション固有の例外を定義
  • 配置ルール: グローバルに配置。ドメイン固有の例外は app/Domains/{Domain}/Exceptions/ に配置することも検討
  • 使用例: InsufficientInventoryException, InvalidPaymentException, OrderNotFoundException など
  • 使用場面: ビジネスロジックエラーを明確に表現する場合

Broadcast (app/Broadcast/)

  • 役割: ブロードキャストチャンネルを配置。リアルタイム通信(WebSocket等)用のチャンネル定義
  • 配置ルール: グローバルに配置
  • 使用例: 注文状況のリアルタイム更新、通知のリアルタイム配信など
  • 注意: Laravel Echo、Pusher、Socket.IO等と連携して使用

Notifications (app/Notifications/)

  • 役割: 通知クラスを配置。メール、SMS等の通知を定義
  • 配置ルール: グローバルに配置
  • : TwoFactorEmailCodeNotification.php(2FAメール認証コード通知)
  • 注意: Mailableクラス(app/Mail/)とは異なる。通知はより高レベルな抽象化で、複数のチャネル(メール、SMS、データベース等)に対応可能

config/ (config/)

  • 役割: 設定ファイルを配置。アプリケーションの設定値を定義
  • 配置ルール: Laravel標準に従い、ドメイン別に分割(例: config/catalog.php, config/orders.php, config/subscriptions.php
  • 注意: アプリケーション内では config('catalog.xxx') 形式で参照。env() 関数は設定ファイル内のみで使用し、アプリケーション内では config() を使用
  • : app.php, auth.php, cache.php, database.php, filament.php, filesystems.php, livewire.php, logging.php, mail.php, queue.php, services.php, session.php, theme.php

プロジェクトルート配下のディレクトリ

database/ (database/)

  • 役割: データベース関連ファイルを配置。マイグレーション、シーダー、ファクトリーを定義
  • 配置ルール: Laravel標準の構造に従う
  • factories/: モデルファクトリー。テストデータ生成用
  • migrations/: データベースマイグレーション。スキーマ変更を定義
  • seeders/: データベースシーダー。初期データ投入用
  • 注意: マイグレーションは時系列で管理され、YYYY_MM_DD_HHMMSS_create_table_name.php 形式で命名

routes/ (routes/)

  • 役割: ルート定義ファイルを配置。HTTPリクエストのルーティングを定義
  • 配置ルール: Laravel標準の構造に従う
  • web.php: Webルート(セッション、CSRF保護、Cookie暗号化が有効)
  • console.php: Artisanコマンドのスケジュール定義
  • 注意: APIルートは routes/api.php に配置(現在は未使用)。Laravel 12では bootstrap/app.php でルートファイルを登録

resources/ (resources/)

  • 役割: 未コンパイルのアセット(ビュー、言語ファイル、CSS等)を配置
  • 配置ルール: Laravel標準の構造に従う
  • views/: Bladeビューテンプレート。app/Admin/app/Frontend/ 配下のビューは近接配置ルールに従う
  • lang/: 多言語翻訳ファイル。{locale}/{group}.php 形式で配置
  • css/: CSSファイル。Tailwind CSSの設定ファイル等
  • 注意: app/Admin/ 配下のビューは近接配置ルールに従い、app/Admin/.../Views/ に配置。resources/views/ は標準的なLaravelビュー用

bootstrap/ (bootstrap/)

  • 役割: アプリケーション起動時の設定ファイルを配置
  • 配置ルール: Laravel標準の構造に従う
  • app.php: アプリケーション起動設定。ミドルウェア、例外処理、ルートファイルの登録
  • providers.php: サービスプロバイダの登録
  • cache/: 設定キャッシュ、ルートキャッシュ等(自動生成)
  • 注意: Laravel 12では app.php でミドルウェアやルートを登録。providers.php でサービスプロバイダを登録

tests/ (tests/)

  • 役割: PHPUnitテストファイルを配置。アプリケーションのテストを定義
  • 配置ルール: Laravel標準の構造に従う
  • Feature/: フィーチャーテスト。HTTPリクエスト、データベース操作を含む統合テスト
  • Unit/: ユニットテスト。単一のクラスやメソッドのテスト
  • 注意: テストは php artisan test で実行。RefreshDatabase トレイトを使用する場合は、テスト専用データベースを使用すること

storage/ (storage/)

  • 役割: アプリケーションが生成するファイルを配置。ログ、キャッシュ、アップロードファイル等
  • 配置ルール: Laravel標準の構造に従う
  • app/: アプリケーションが生成するファイル(公開されない)
  • framework/: フレームワークが生成するファイル(キャッシュ、セッション等)
  • logs/: ログファイル
  • 注意: .gitignore に含まれているため、バージョン管理されない

public/ (public/)

  • 役割: 公開ファイルを配置。Webサーバーのドキュメントルート
  • 配置ルール: Laravel標準の構造に従う
  • index.php: アプリケーションのエントリーポイント
  • assets/: コンパイル済みアセット(CSS、JavaScript、画像等)
  • 注意: Viteでコンパイルされたアセットはここに配置される

ヘルパー関数・Service・Facadeの使い分け

ヘルパー関数、Service、Facadeの使い分けを明確にするため、以下の基準に従います。

使い分けの判断フローチャート

1. 状態を持つ? → Yes → Service
                ↓ No
2. 複数のメソッドが必要? → Yes → Service
                          ↓ No
3. 複雑なビジネスロジック? → Yes → Service
                            ↓ No
4. 単純な変換・フォーマット? → Yes → ヘルパー関数
                              ↓ No
5. Blade/Livewireから直接呼ぶ必要がある? → Yes → Facade(Serviceをラップ)
                                                ↓ No
                                                → Service(依存注入)

比較表

観点 ヘルパー関数 Service Facade
役割 純粋関数(状態なし、単純な変換) ビジネスロジック(状態あり、複数メソッド) Serviceへの静的アクセスラッパー
状態 持たない 持つ可能性がある(セッション、DB、キャッシュ等) Serviceの状態を参照
メソッド数 単一関数 複数メソッド Serviceのメソッドをラップ
呼び出し方法 グローバル関数 依存注入 静的メソッド
使用場所 どこからでも コントローラ、サービス、テスト Blade、Livewire(最小限)
テスト容易性 高い(純粋関数) 高い(モック注入可能) やや低い(モック化が必要)
依存関係 なし 明確(コンストラクタで注入) 隠れる(静的呼び出し)
使用例 get_image_url(), date_time_format(), theme_asset() CartService::addToCart(), PageCompiler::compile() Cart::addToCart()(Blade/Livewireから)
推奨度 適切な場合に使用 基本はこちらを使用 最小限の使用(Blade/Livewireから直接呼ぶ場合のみ)

実践的なルール

ヘルパー関数を使う場合

  • ✅ 画像URL生成、日付フォーマット、文字列操作など
  • ✅ 純粋関数(同じ入力→同じ出力)
  • ✅ 状態を持たない

Serviceを使う場合

  • ✅ カート管理、注文処理、ページコンパイルなど
  • ✅ 状態を持つ(セッション、DB、キャッシュ)
  • ✅ 複数のメソッドを持つ
  • ✅ ビジネスロジックを含む
  • 基本はこちらを使用(コントローラ、サービス、テストでは依存注入)

Facadeを使う場合

  • ✅ BladeテンプレートやLivewireコンポーネントから直接呼ぶ必要がある場合のみ
  • ✅ Laravel標準のFacade(Cache、DB、Auth等)と同じような使い方
  • ⚠️ 最小限の使用(それ以外はServiceを依存注入で使用)

関連ドキュメント