# Laravel Module Pattern Template

## Overview
This document defines the standard pattern for creating Laravel modules with best practices, SOLID principles, and clean architecture.

## 📁 Module Structure

```
app/Modules/{ModuleName}/
├── controllers/
│   ├── api/
│   │   └── v1/
│   └── {ModuleName}Controller.php
├── models/
│   └── {ModuleName}Model.php
├── services/
│   ├── {ModuleName}ServiceInterface.php
│   ├── {ModuleName}Service.php
│   └── {ModuleName}AuthService.php (if needed)
├── views/
│   └── {module_name}.blade.php
├── routes.php
├── {ModuleName}ServiceProvider.php
└── README.md
```

## 🏗️ Service Provider Pattern

### Template: `{ModuleName}ServiceProvider.php`

```php
<?php

namespace App\Modules\{ModuleName};

use App\Modules\{ModuleName}\Services\{ModuleName}Service;
use App\Modules\{ModuleName}\Services\{ModuleName}ServiceInterface;
use App\Modules\{ModuleName}\Services\{ModuleName}AuthService;

class {ModuleName}ServiceProvider extends \App\Modules\ServiceProvider
{
    public function register()
    {
        parent::register('{module_name}');

        // Bind service interface to implementation as singleton (stateless service)
        $this->app->singleton({ModuleName}ServiceInterface::class, {ModuleName}Service::class);

        // Register auth service as singleton (if needed)
        $this->app->singleton({ModuleName}AuthService::class);

        // Optional: Register alias for convenience
        $this->app->alias({ModuleName}ServiceInterface::class, '{module_name}');
    }

    public function boot()
    {
        parent::boot('{module_name}');
    }
}
```

## 🔧 Service Interface Pattern

### Template: `{ModuleName}ServiceInterface.php`

```php
<?php

namespace App\Modules\{ModuleName}\Services;

interface {ModuleName}ServiceInterface
{
    /**
     * Main service method
     */
    public function processData($data): array;

    /**
     * Get data by ID
     */
    public function getById($id): array;

    /**
     * Create new record
     */
    public function create($data): array;

    /**
     * Update existing record
     */
    public function update($id, $data): array;

    /**
     * Delete record
     */
    public function delete($id): bool;

    /**
     * List all records
     */
    public function getAll(): array;
}
```

## 🚀 Service Implementation Pattern

### Template: `{ModuleName}Service.php`

```php
<?php

namespace App\Modules\{ModuleName}\Services;

use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Config;

class {ModuleName}Service implements {ModuleName}ServiceInterface
{
    protected $authService;
    protected $config;

    public function __construct({ModuleName}AuthService $authService)
    {
        $this->authService = $authService;
        $this->config = Config::get('{module_name}');
    }

    /**
     * Main service method
     */
    public function processData($data): array
    {
        try {
            $this->logInfo('Processing data', ['data_id' => $data->id ?? 'unknown']);
            
            // Validate authentication
            $token = $this->getValidToken();
            
            // Process the data
            $result = $this->performOperation($data, $token);
            
            $this->logInfo('Data processed successfully', ['result' => $result]);
            
            return [
                'success' => true,
                'data' => $result,
                'message' => 'Operation completed successfully'
            ];
            
        } catch (Exception $e) {
            $this->logError('Failed to process data', [
                'error' => $e->getMessage(),
                'data' => $data ?? null
            ]);
            
            return [
                'success' => false,
                'error' => $e->getMessage()
            ];
        }
    }

    /**
     * Get data by ID
     */
    public function getById($id): array
    {
        try {
            $this->logInfo('Getting data by ID', ['id' => $id]);
            
            // Implementation here
            
            return ['success' => true, 'data' => $data];
            
        } catch (Exception $e) {
            $this->logError('Failed to get data by ID', ['id' => $id, 'error' => $e->getMessage()]);
            return ['success' => false, 'error' => $e->getMessage()];
        }
    }

    /**
     * Create new record
     */
    public function create($data): array
    {
        try {
            $this->logInfo('Creating new record', ['data' => $data]);
            
            // Implementation here
            
            return ['success' => true, 'id' => $id];
            
        } catch (Exception $e) {
            $this->logError('Failed to create record', ['error' => $e->getMessage()]);
            return ['success' => false, 'error' => $e->getMessage()];
        }
    }

    /**
     * Update existing record
     */
    public function update($id, $data): array
    {
        try {
            $this->logInfo('Updating record', ['id' => $id, 'data' => $data]);
            
            // Implementation here
            
            return ['success' => true];
            
        } catch (Exception $e) {
            $this->logError('Failed to update record', ['id' => $id, 'error' => $e->getMessage()]);
            return ['success' => false, 'error' => $e->getMessage()];
        }
    }

    /**
     * Delete record
     */
    public function delete($id): bool
    {
        try {
            $this->logInfo('Deleting record', ['id' => $id]);
            
            // Implementation here
            
            return true;
            
        } catch (Exception $e) {
            $this->logError('Failed to delete record', ['id' => $id, 'error' => $e->getMessage()]);
            return false;
        }
    }

    /**
     * List all records
     */
    public function getAll(): array
    {
        try {
            $this->logInfo('Getting all records');
            
            // Implementation here
            
            return ['success' => true, 'data' => $data];
            
        } catch (Exception $e) {
            $this->logError('Failed to get all records', ['error' => $e->getMessage()]);
            return ['success' => false, 'error' => $e->getMessage()];
        }
    }

    /**
     * Get valid authentication token
     */
    protected function getValidToken()
    {
        try {
            return $this->authService->getValidToken();
        } catch (Exception $e) {
            $this->logError('Authentication failed', ['error' => $e->getMessage()]);
            throw new Exception('Authentication failed: ' . $e->getMessage());
        }
    }

    /**
     * Centralized logging methods
     */
    protected function logInfo($message, array $context = []): void
    {
        Log::info("[{MODULE_NAME}] " . $message, $context);
    }

    protected function logError($message, array $context = []): void
    {
        Log::error("[{MODULE_NAME}] " . $message, $context);
    }

    protected function logWarning($message, array $context = []): void
    {
        Log::warning("[{MODULE_NAME}] " . $message, $context);
    }
}
```

## 🔐 Authentication Service Pattern

### Template: `{ModuleName}AuthService.php`

```php
<?php

namespace App\Modules\{ModuleName}\Services;

use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Cache;

class {ModuleName}AuthService
{
    protected $config;
    protected $baseUrl;
    protected $clientId;
    protected $clientSecret;

    public function __construct()
    {
        $this->config = Config::get('{module_name}');
        $this->baseUrl = $this->config['base_url'] ?? '';
        $this->clientId = $this->config['client_id'] ?? '';
        $this->clientSecret = $this->config['client_secret'] ?? '';
    }

    /**
     * Get valid authentication token
     */
    public function getValidToken()
    {
        $cacheKey = '{module_name}_auth_token';
        
        // Check if token exists in cache
        if (Cache::has($cacheKey)) {
            $token = Cache::get($cacheKey);
            if ($this->isTokenValid($token)) {
                return $token;
            }
        }

        // Get new token
        return $this->refreshToken();
    }

    /**
     * Refresh authentication token
     */
    protected function refreshToken()
    {
        try {
            Log::info("[{MODULE_NAME}] Refreshing authentication token");
            
            // Implementation for token refresh
            
            $token = $this->requestNewToken();
            
            // Cache the token
            Cache::put('{module_name}_auth_token', $token, now()->addHours(1));
            
            Log::info("[{MODULE_NAME}] Token refreshed successfully");
            
            return $token;
            
        } catch (Exception $e) {
            Log::error("[{MODULE_NAME}] Failed to refresh token", ['error' => $e->getMessage()]);
            throw new Exception('Failed to refresh authentication token: ' . $e->getMessage());
        }
    }

    /**
     * Check if token is still valid
     */
    protected function isTokenValid($token): bool
    {
        // Implementation to validate token
        return !empty($token) && isset($token->expires_at) && $token->expires_at > now();
    }

    /**
     * Request new token from API
     */
    protected function requestNewToken()
    {
        // Implementation for requesting new token
        // This will vary based on the authentication method
    }
}
```

## ⚙️ Configuration Pattern

### Template: `config/{module_name}.php`

```php
<?php

return [
    /*
    |--------------------------------------------------------------------------
    | {ModuleName} Configuration
    |--------------------------------------------------------------------------
    |
    | Configuration settings for {ModuleName} module
    |
    */

    // API Configuration
    'base_url' => env('{MODULE_NAME}_BASE_URL', 'https://api.example.com'),
    'timeout' => env('{MODULE_NAME}_TIMEOUT', 30),
    
    // Authentication
    'client_id' => env('{MODULE_NAME}_CLIENT_ID'),
    'client_secret' => env('{MODULE_NAME}_CLIENT_SECRET'),
    
    // Default Settings
    'defaults' => [
        'status' => 'active',
        'type' => 'standard',
    ],
    
    // Logging
    'log_prefix' => '[{MODULE_NAME}]',
    'log_level' => env('{MODULE_NAME}_LOG_LEVEL', 'info'),
    
    // Cache Settings
    'cache_ttl' => env('{MODULE_NAME}_CACHE_TTL', 3600), // 1 hour
    
    // API Endpoints
    'endpoints' => [
        'base' => '/api/v1',
        'auth' => '/oauth/token',
        'data' => '/data',
    ],
];
```

## 🧪 Testing Pattern

### Template: `tests/Feature/{ModuleName}Test.php`

```php
<?php

namespace Tests\Feature;

use Tests\TestCase;
use App\Modules\{ModuleName}\Services\{ModuleName}ServiceInterface;
use App\Modules\{ModuleName}\Services\{ModuleName}AuthService;
use Illuminate\Foundation\Testing\RefreshDatabase;

class {ModuleName}Test extends TestCase
{
    use RefreshDatabase;

    public function test_service_interface_binding()
    {
        $service = app({ModuleName}ServiceInterface::class);
        
        $this->assertInstanceOf({ModuleName}ServiceInterface::class, $service);
    }

    public function test_auth_service_singleton()
    {
        $auth1 = app({ModuleName}AuthService::class);
        $auth2 = app({ModuleName}AuthService::class);
        
        $this->assertSame($auth1, $auth2);
    }

    public function test_service_alias_binding()
    {
        $service = app('{module_name}');
        
        $this->assertInstanceOf({ModuleName}ServiceInterface::class, $service);
    }

    public function test_process_data_method()
    {
        $service = app({ModuleName}ServiceInterface::class);
        
        $result = $service->processData(['test' => 'data']);
        
        $this->assertIsArray($result);
        $this->assertArrayHasKey('success', $result);
    }
}
```

## 📝 Registration Steps

### 1. Update `config/app.php`

```php
'providers' => [
    // ... other providers
    'App\Modules\{ModuleName}\{ModuleName}ServiceProvider',
],
```

### 2. Create Environment Variables

```env
# {ModuleName} Configuration
{MODULE_NAME}_BASE_URL=https://api.example.com
{MODULE_NAME}_CLIENT_ID=your_client_id
{MODULE_NAME}_CLIENT_SECRET=your_client_secret
{MODULE_NAME}_TIMEOUT=30
{MODULE_NAME}_LOG_LEVEL=info
{MODULE_NAME}_CACHE_TTL=3600
```

### 3. Clear Cache

```bash
docker-compose exec app php artisan config:clear
docker-compose exec app php artisan cache:clear
```

## ✅ Best Practices Checklist

- [ ] **Interface-based dependency injection**
- [ ] **Singleton binding for stateless services**
- [ ] **Proper service provider naming** (`{ModuleName}ServiceProvider`)
- [ ] **Centralized authentication service**
- [ ] **Configuration in separate config file**
- [ ] **Comprehensive logging with prefixes**
- [ ] **Error handling with try-catch blocks**
- [ ] **Return type declarations**
- [ ] **Service alias registration**
- [ ] **Unit tests for service binding**
- [ ] **Environment variable configuration**
- [ ] **Documentation in README.md**

## 🔄 Migration Pattern for Existing Modules

When refactoring existing modules:

1. **Create interface** for the main service
2. **Extract authentication** to separate service
3. **Move constants** to config file
4. **Add return types** to all methods
5. **Implement singleton binding**
6. **Add comprehensive logging**
7. **Create unit tests**
8. **Update documentation**

## 📚 Example: Xero Module Implementation

See `app/Modules/Xero/` for a complete implementation of this pattern.

## 🎯 Benefits

- **Consistency**: All modules follow the same pattern
- **Testability**: Easy to mock interfaces
- **Maintainability**: Clear separation of concerns
- **Performance**: Singleton binding for stateless services
- **Flexibility**: Easy to swap implementations
- **Debugging**: Comprehensive logging with prefixes 