XOOPSのサービスレイヤーパターン
2.5.x ✅ 4.0.x ✅
サービスレイヤーパターンはビジネスロジックを専用のサービスクラスにカプセル化し、コントローラーとデータアクセスレイヤー間の明確な分離を提供します。このパターンはコード再利用性、テスト可能性、保守性を促進します。
サービスレイヤーの概念
Section titled “サービスレイヤーの概念”サービスレイヤー:
- ドメインビジネスロジックを含む
- 複数のリポジトリを調整
- 複雑な操作を処理
- トランザクションを管理
- 検証と認可を実行
- コントローラーに高度な操作を提供
- 複数のコントローラー全体で再利用可能なビジネスロジック
- 分離してテストしやすい
- 一元化されたビジネスルール実装
- 明確な関心事の分離
- 簡略化されたコントローラーコード
<?php// 注入された依存関係を持つサービスclass UserService{ private $userRepository; private $emailService;
public function __construct( UserRepositoryInterface $userRepository, EmailServiceInterface $emailService ) { $this->userRepository = $userRepository; $this->emailService = $emailService; }
public function registerUser($username, $email, $password) { // 検証 $this->validate($username, $email, $password);
// ユーザーを作成 $user = new User(); $user->setUsername($username); $user->setEmail($email); $user->setPassword($password);
// 保存 $userId = $this->userRepository->save($user);
// ウェルカムメールを送信 $this->emailService->sendWelcome($email, $username);
return $userId; }
private function validate($username, $email, $password) { $errors = [];
if (strlen($username) < 3) { $errors['username'] = 'ユーザー名が短すぎます'; }
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { $errors['email'] = '無効なメール'; }
if (strlen($password) < 6) { $errors['password'] = 'パスワードが短すぎます'; }
if (!empty($errors)) { throw new ValidationException('無効な入力', $errors); } }}?>サービスコンテナー
Section titled “サービスコンテナー”<?phpclass ServiceContainer{ private $services = [];
public function __construct($db) { // リポジトリを登録 $this->services['userRepository'] = new UserRepository($db);
// サービスを登録 $this->services['userService'] = new UserService( $this->services['userRepository'] ); }
public function get($name) { if (!isset($this->services[$name])) { throw new \InvalidArgumentException("サービスが見つかりません: $name"); } return $this->services[$name]; }}?>コントローラーでの使用
Section titled “コントローラーでの使用”<?phpclass UserController{ private $userService;
public function __construct(ServiceContainer $container) { $this->userService = $container->get('userService'); }
public function registerAction() { if ($_SERVER['REQUEST_METHOD'] !== 'POST') { return []; }
try { $userId = $this->userService->registerUser( $_POST['username'], $_POST['email'], $_POST['password'] );
return [ 'success' => true, 'userId' => $userId, ]; } catch (ValidationException $e) { return [ 'success' => false, 'errors' => $e->getErrors(), ]; } }}?>ベストプラクティス
Section titled “ベストプラクティス”- 各サービスはドメイン関心を1つ処理
- サービスは実装ではなくインターフェースに依存
- 依存関係にコンストラクタ注入を使用
- サービスは分離してテストできるべき
- ドメイン固有の例外をスロー
- サービスはHTTPリクエストの詳細に依存しないべき
- サービスはフォーカスされ、結合度は高い
関連ドキュメント
Section titled “関連ドキュメント”関連トピック:
- MVC-Pattern - コントローラー統合
- Repository-Pattern - データアクセス
- DTO-Pattern - データ転送オブジェクト
- Testing - サービステスト
タグ: #service-layer #business-logic #dependency-injection #design-patterns