commit 08-2025

This commit is contained in:
Kevin Adametz 2025-08-12 18:01:59 +02:00
parent 9ae662f63e
commit 480fdc65ed
404 changed files with 65310 additions and 2600431 deletions

View file

@ -0,0 +1,290 @@
<?php
namespace App\Domain;
use App\Models\UserShop;
/**
* Domain Context - Immutable context object representing current domain state
*
* This class provides a clean, immutable way to pass domain context
* throughout the application without relying on global state.
*/
class DomainContext
{
public function __construct(
public readonly string $type, // 'main', 'main-shop', 'crm', 'portal', 'checkout', 'user-shop', 'unknown'
public readonly string $fullDomain, // Complete domain (e.g., 'shop.mivita.care')
public readonly string $domain, // Base domain (e.g., 'mivita')
public readonly ?string $subdomain, // Subdomain part (e.g., 'shop') or null
public readonly string $tld, // TLD (e.g., '.care')
public readonly ?UserShop $userShop = null, // For user shop contexts
public readonly array $config = [] // Additional configuration
) {}
/**
* Check if this is the main domain (mivita.care)
*/
public function isMainDomain(): bool
{
return $this->type === 'main';
}
/**
* Check if this is the main shop domain (mivita.shop)
*/
public function isMainShopDomain(): bool
{
return $this->type === 'main-shop';
}
/**
* Check if this is any main domain variant
*/
public function isAnyMainDomain(): bool
{
return $this->isMainDomain() || $this->isMainShopDomain();
}
/**
* Check if this is the CRM domain (my.mivita.care)
*/
public function isCrmDomain(): bool
{
return $this->type === 'crm';
}
/**
* Check if this is the portal domain (in.mivita.care)
*/
public function isPortalDomain(): bool
{
return $this->type === 'portal';
}
/**
* Check if this is the checkout domain (checkout.mivita.care)
*/
public function isCheckoutDomain(): bool
{
return $this->type === 'checkout';
}
/**
* Check if this is a user shop domain ({slug}.mivita.care)
*/
public function isUserShopDomain(): bool
{
return $this->type === 'user-shop';
}
/**
* Check if this is an unknown/invalid domain
*/
public function isUnknownDomain(): bool
{
return $this->type === 'unknown';
}
/**
* Check if this domain supports e-commerce functionality
*/
public function supportsEcommerce(): bool
{
return in_array($this->type, ['main-shop', 'user-shop']);
}
/**
* Check if this domain requires authentication
*/
public function requiresAuthentication(): bool
{
return in_array($this->type, ['crm', 'portal']);
}
/**
* Get the appropriate route prefix for this domain
*/
public function getRoutePrefix(): ?string
{
return match($this->type) {
'crm' => 'crm',
'portal' => 'portal',
'checkout' => 'checkout',
'user-shop' => 'shop',
default => null
};
}
/**
* Get the user shop slug (if applicable)
*/
public function getUserShopSlug(): ?string
{
return $this->userShop?->slug ?? $this->subdomain;
}
/**
* Get the middleware stack appropriate for this domain
*/
public function getMiddlewareStack(): array
{
$middleware = ['web']; // Base middleware
switch ($this->type) {
case 'crm':
$middleware[] = 'auth';
break;
case 'portal':
$middleware[] = 'auth:customers';
break;
case 'checkout':
$middleware[] = 'checkout';
break;
case 'user-shop':
$middleware[] = 'subdomain';
break;
}
return $middleware;
}
/**
* Get the view namespace for this domain
*/
public function getViewNamespace(): ?string
{
return match($this->type) {
'crm' => 'crm',
'portal' => 'portal',
'checkout' => 'checkout',
'user-shop' => 'shop',
default => null
};
}
/**
* Get the layout template for this domain
*/
public function getLayoutTemplate(): string
{
return match($this->type) {
'crm' => 'layouts.crm',
'portal' => 'layouts.portal',
'checkout' => 'layouts.checkout',
'user-shop' => 'web.layouts.application',
'main-shop' => 'web.layouts.application',
default => 'layouts.app'
};
}
/**
* Check if this domain allows cart functionality
*/
public function allowsCart(): bool
{
return $this->supportsEcommerce();
}
/**
* Get session configuration for this domain
*/
public function getSessionConfig(): array
{
$config = [
'lifetime' => config('session.lifetime'),
'expire_on_close' => config('session.expire_on_close'),
];
// Set domain-specific session domain
if ($this->isUserShopDomain()) {
$config['domain'] = '.' . $this->domain . $this->tld;
}
return $config;
}
/**
* Create a new context with updated user shop
*/
public function withUserShop(UserShop $userShop): self
{
return new self(
$this->type,
$this->fullDomain,
$this->domain,
$this->subdomain,
$this->tld,
$userShop,
$this->config
);
}
/**
* Create a new context with additional configuration
*/
public function withConfig(array $config): self
{
return new self(
$this->type,
$this->fullDomain,
$this->domain,
$this->subdomain,
$this->tld,
$this->userShop,
array_merge($this->config, $config)
);
}
/**
* Convert to array for debugging/logging
*/
public function toArray(): array
{
return [
'type' => $this->type,
'full_domain' => $this->fullDomain,
'domain' => $this->domain,
'subdomain' => $this->subdomain,
'tld' => $this->tld,
'user_shop_slug' => $this->getUserShopSlug(),
'user_shop_id' => $this->userShop?->id,
'config' => $this->config,
];
}
/**
* Get a human-readable description of this domain context
*/
public function getDescription(): string
{
return match($this->type) {
'main' => 'Main Website',
'main-shop' => 'Main Shop',
'crm' => 'CRM System',
'portal' => 'Customer Portal',
'checkout' => 'Checkout System',
'user-shop' => "User Shop ({$this->getUserShopSlug()})",
'unknown' => 'Unknown Domain',
default => 'Undefined Domain Type'
};
}
/**
* Static factory method to create context from domain service
*/
public static function fromDomainInfo(array $domainInfo, ?UserShop $userShop = null): self
{
return new self(
$domainInfo['type'],
$domainInfo['full_domain'],
$domainInfo['domain'],
$domainInfo['subdomain'],
$domainInfo['tld'],
$userShop
);
}
}

View file

@ -0,0 +1,261 @@
<?php
namespace App\Http\Middleware;
use App\Domain\DomainContext;
use App\Services\DomainService;
use App\Services\Util;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Session;
/**
* Enhanced Domain Resolver Middleware
*
* This middleware replaces the current Subdomain middleware with a more
* robust, maintainable solution that properly handles all domain types.
*/
class DomainResolver
{
private DomainService $domainService;
public function __construct(DomainService $domainService)
{
$this->domainService = $domainService;
}
/**
* Handle an incoming request
*/
public function handle(Request $request, Closure $next)
{
// Parse the domain from the request
$host = $request->getHost();
$domainInfo = $this->domainService->parseDomain($host);
// Create domain context
$context = $this->createDomainContext($request, $domainInfo);
// Handle unknown domains
if ($context->isUnknownDomain()) {
return $this->handleUnknownDomain($request, $context);
}
// Set up the application context
$this->setupApplicationContext($request, $context);
// Store context in the application container
app()->instance('domain.context', $context);
return $next($request);
}
/**
* Create domain context from parsed domain information
*/
private function createDomainContext(Request $request, array $domainInfo): DomainContext
{
$userShop = null;
// Load user shop for user-shop domains
if ($domainInfo['type'] === 'user-shop' && $domainInfo['subdomain']) {
$userShop = $this->domainService->getUserShop($domainInfo['subdomain']);
// If user shop is invalid, mark as unknown
if (!$userShop) {
$domainInfo['type'] = 'unknown';
}
}
// Handle main-shop domain with default shop
if ($domainInfo['type'] === 'main-shop') {
$userShop = $this->domainService->getDefaultUserShop();
}
return DomainContext::fromDomainInfo($domainInfo, $userShop);
}
/**
* Handle unknown or invalid domains
*/
private function handleUnknownDomain(Request $request, DomainContext $context)
{
// Log the invalid domain attempt
\Log::warning('Unknown domain accessed', [
'host' => $request->getHost(),
'domain_info' => $context->toArray(),
'user_agent' => $request->userAgent(),
'ip' => $request->ip()
]);
// Redirect to main domain
$mainUrl = $this->domainService->buildUrl('main');
return redirect($mainUrl, 301);
}
/**
* Set up application context based on domain
*/
private function setupApplicationContext(Request $request, DomainContext $context): void
{
switch ($context->type) {
case 'user-shop':
$this->setupUserShopContext($request, $context);
break;
case 'main-shop':
$this->setupMainShopContext($request, $context);
break;
case 'crm':
$this->setupCrmContext($request, $context);
break;
case 'portal':
$this->setupPortalContext($request, $context);
break;
case 'checkout':
$this->setupCheckoutContext($request, $context);
break;
case 'main':
$this->setupMainContext($request, $context);
break;
}
// Set up common configurations
$this->setupCommonContext($request, $context);
}
/**
* Set up context for user shop domains
*/
private function setupUserShopContext(Request $request, DomainContext $context): void
{
if (!$context->userShop) {
return;
}
// Set up session data (maintaining compatibility with current implementation)
Session::put('user_shop', $context->userShop);
Session::put('user_shop_domain', $context->fullDomain);
// Set dynamic URL configuration
Config::set('app.url', $context->fullDomain);
// Set route prefix for utilities (maintaining compatibility)
Util::setPostRoute('user/');
// Remove subdomain parameter from route (maintaining compatibility)
if ($request->route() && $request->route()->hasParameter('subdomain')) {
$request->route()->forgetParameter('subdomain');
}
}
/**
* Set up context for main shop domain (mivita.shop)
*/
private function setupMainShopContext(Request $request, DomainContext $context): void
{
if (!$context->userShop) {
return;
}
// Set up session data similar to user shops
Session::put('user_shop', $context->userShop);
Session::put('user_shop_domain', $context->fullDomain);
// Set dynamic URL configuration
Config::set('app.url', $context->fullDomain);
// Set route prefix
Util::setPostRoute('user/');
}
/**
* Set up context for CRM domain (my.mivita.care)
*/
private function setupCrmContext(Request $request, DomainContext $context): void
{
// Set up CRM-specific configurations
Config::set('app.url', $context->fullDomain);
// Set session domain for cross-subdomain compatibility
Config::set('session.domain', '.' . $context->domain . $context->tld);
}
/**
* Set up context for portal domain (in.mivita.care)
*/
private function setupPortalContext(Request $request, DomainContext $context): void
{
// Set up portal-specific configurations
Config::set('app.url', $context->fullDomain);
// Set session domain for cross-subdomain compatibility
Config::set('session.domain', '.' . $context->domain . $context->tld);
}
/**
* Set up context for checkout domain (checkout.mivita.care)
*/
private function setupCheckoutContext(Request $request, DomainContext $context): void
{
// Set up checkout-specific configurations
Config::set('app.url', $context->fullDomain);
// Set session domain for cross-subdomain compatibility
Config::set('session.domain', '.' . $context->domain . $context->tld);
}
/**
* Set up context for main domain (mivita.care)
*/
private function setupMainContext(Request $request, DomainContext $context): void
{
// Set up main domain configurations
Config::set('app.url', $context->fullDomain);
}
/**
* Set up common context configurations
*/
private function setupCommonContext(Request $request, DomainContext $context): void
{
// Set up view namespace if applicable
if ($namespace = $context->getViewNamespace()) {
// This could be used by view composers or other components
Config::set('view.domain_namespace', $namespace);
}
// Set up any domain-specific cache prefixes
if ($context->isUserShopDomain()) {
Config::set('cache.prefix', config('cache.prefix') . '_' . $context->getUserShopSlug());
}
// Set up CSRF token domain
if (in_array($context->type, ['crm', 'portal', 'checkout'])) {
Config::set('session.same_site', 'lax');
}
}
/**
* Validate that the domain is properly configured for this request
*/
private function validateDomainConfiguration(DomainContext $context): bool
{
$errors = $this->domainService->validateConfiguration();
if (!empty($errors)) {
\Log::error('Domain configuration errors', [
'errors' => $errors,
'context' => $context->toArray()
]);
return false;
}
return true;
}
}

View file

@ -0,0 +1,272 @@
<?php
namespace App\Services;
use App\Models\UserShop;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Config;
/**
* Domain Service - Centralized domain and subdomain management
*
* This service provides a centralized way to handle domain resolution,
* subdomain validation, and domain-specific configuration management.
*/
class DomainService
{
private const CACHE_TTL = 3600; // 1 hour
private const FIXED_SUBDOMAINS = ['my', 'in', 'checkout'];
private array $domainConfig;
public function __construct()
{
$this->domainConfig = $this->loadDomainConfiguration();
}
/**
* Load domain configuration from config files
*/
private function loadDomainConfiguration(): array
{
return [
'main_domain' => config('app.domain'),
'main_tld' => config('app.tld_care'),
'shop_tld' => config('app.tld_shop'),
'protocol' => config('app.protocol'),
'subdomains' => [
'crm' => config('app.pre_url_crm', 'my.'),
'portal' => config('app.pre_url_portal', 'in.'),
'checkout' => config('app.pre_url_checkout', 'checkout.'),
]
];
}
/**
* Determine the type of subdomain
*/
public function getSubdomainType(string $subdomain): string
{
// Check if it's a fixed subdomain
if (in_array($subdomain, self::FIXED_SUBDOMAINS)) {
return match($subdomain) {
'my' => 'crm',
'in' => 'portal',
'checkout' => 'checkout',
default => 'unknown'
};
}
// Check if it's a valid user shop
if ($this->isValidUserShop($subdomain)) {
return 'user-shop';
}
return 'unknown';
}
/**
* Check if a subdomain represents a valid user shop
*/
public function isValidUserShop(string $slug): bool
{
$cacheKey = "user_shop_valid_{$slug}";
return Cache::remember($cacheKey, self::CACHE_TTL, function () use ($slug) {
$userShop = UserShop::where('slug', $slug)->first();
if (!$userShop) {
return false;
}
if (!$userShop->active) {
return false;
}
if (!$userShop->user) {
return false;
}
if (!$userShop->user->isActiveShop()) {
return false;
}
return true;
});
}
/**
* Get user shop by slug with caching
*/
public function getUserShop(string $slug): ?UserShop
{
if (!$this->isValidUserShop($slug)) {
return null;
}
$cacheKey = "user_shop_{$slug}";
return Cache::remember($cacheKey, self::CACHE_TTL, function () use ($slug) {
return UserShop::where('slug', $slug)
->with('user')
->first();
});
}
/**
* Parse domain from request and determine context
*/
public function parseDomain(string $host): array
{
$parts = explode('.', $host);
// Handle different TLD scenarios
if (count($parts) < 2) {
return [
'type' => 'invalid',
'domain' => $host,
'subdomain' => null,
'tld' => null
];
}
// Extract TLD and domain
$tld = '.' . end($parts);
$domain = $parts[count($parts) - 2];
// Check for subdomain
$subdomain = null;
if (count($parts) > 2) {
$subdomain = $parts[0];
}
// Determine domain type
$type = $this->determineDomainType($domain, $subdomain, $tld);
return [
'type' => $type,
'domain' => $domain,
'subdomain' => $subdomain,
'tld' => $tld,
'full_domain' => $host
];
}
/**
* Determine domain type based on domain, subdomain, and TLD
*/
private function determineDomainType(string $domain, ?string $subdomain, string $tld): string
{
// Check if it's the main domain
if ($domain === $this->domainConfig['main_domain']) {
if ($subdomain === null) {
// Main domain without subdomain
if ($tld === $this->domainConfig['shop_tld']) {
return 'main-shop'; // mivita.shop
}
return 'main'; // mivita.care
}
// Main domain with subdomain
return $this->getSubdomainType($subdomain);
}
return 'unknown';
}
/**
* Build URL for specific domain type
*/
public function buildUrl(string $type, ?string $path = null, ?string $slug = null): string
{
$base = $this->domainConfig['protocol'];
switch ($type) {
case 'main':
$base .= $this->domainConfig['main_domain'] . $this->domainConfig['main_tld'];
break;
case 'main-shop':
$base .= $this->domainConfig['main_domain'] . $this->domainConfig['shop_tld'];
break;
case 'crm':
$base .= rtrim($this->domainConfig['subdomains']['crm'], '.') .
$this->domainConfig['main_domain'] . $this->domainConfig['main_tld'];
break;
case 'portal':
$base .= rtrim($this->domainConfig['subdomains']['portal'], '.') .
$this->domainConfig['main_domain'] . $this->domainConfig['main_tld'];
break;
case 'checkout':
$base .= rtrim($this->domainConfig['subdomains']['checkout'], '.') .
$this->domainConfig['main_domain'] . $this->domainConfig['main_tld'];
break;
case 'user-shop':
if (!$slug) {
throw new \InvalidArgumentException('Slug required for user-shop URLs');
}
$base .= $slug . '.' . $this->domainConfig['main_domain'] . $this->domainConfig['main_tld'];
break;
default:
throw new \InvalidArgumentException("Unknown domain type: {$type}");
}
if ($path) {
$base .= '/' . ltrim($path, '/');
}
return $base;
}
/**
* Get domain configuration
*/
public function getDomainConfiguration(): array
{
return $this->domainConfig;
}
/**
* Clear user shop cache
*/
public function clearUserShopCache(string $slug): void
{
Cache::forget("user_shop_valid_{$slug}");
Cache::forget("user_shop_{$slug}");
}
/**
* Get default user shop for main domain (fallback)
*/
public function getDefaultUserShop(): ?UserShop
{
return $this->getUserShop('aloevera'); // Current hardcoded fallback
}
/**
* Validate domain configuration
*/
public function validateConfiguration(): array
{
$errors = [];
if (empty($this->domainConfig['main_domain'])) {
$errors[] = 'Main domain not configured';
}
if (empty($this->domainConfig['main_tld'])) {
$errors[] = 'Main TLD not configured';
}
if (empty($this->domainConfig['protocol'])) {
$errors[] = 'Protocol not configured';
}
return $errors;
}
}

View file

@ -0,0 +1,233 @@
# Mivita Multi-Domain & Subdomain Architecture Optimization
## Current Implementation Analysis
### Domain Structure Overview
The mivita.care application uses a multi-domain and subdomain architecture to serve different application areas:
#### Main Domains & Subdomains:
1. **Main Domain**: `mivita.care` (or `mivita.test` in development)
2. **Fixed Subdomains**:
- `my.mivita.care` - CRM/Customer Management System
- `in.mivita.care` - Portal for customers
- `checkout.mivita.care` - Checkout/Payment processing
3. **Dynamic Subdomains**: `{shop-slug}.mivita.care` - Individual user shops
4. **Alternative Domain**: `mivita.shop` - Alternative shopping domain
### Current Architecture Issues
#### 1. Subdomain Middleware Problems
**File**: `app/Http/Middleware/Subdomain.php`
**Issues Identified**:
- Hard-coded shop selection (`'aloevera'`) for main domain
- Mixed responsibility (handles both dynamic shops and main domain logic)
- No proper fallback mechanism for invalid subdomains
- Configuration dependencies scattered across middleware
- Session management directly in middleware
#### 2. Routing Complexity
**Current Structure**:
```
routes/
├── web.php # Main entry (mostly empty)
├── main.php # Main domain routes
├── subdomain.php # Dynamic subdomain routes
├── crm.php # CRM subdomain (my.)
├── portal.php # Portal subdomain (in.)
├── checkout.php # Checkout subdomain (checkout.)
├── api.php # API routes
└── utility.php # Utility routes
```
**Issues**:
- Route duplication across files
- No clear separation of concerns
- Complex domain-based routing in multiple files
- Inconsistent middleware application
#### 3. Configuration Management
**File**: `.env`
**Current Setup**:
```
APP_DOMAIN=mivita
APP_TLD_CARE=.test
APP_TLD_SHOP=.lshop
APP_URL_CHECKOUT=checkout.
APP_URL_CRM=my.
APP_URL_PORTAL=in.
```
**Issues**:
- Environment-dependent configuration
- No centralized domain management
- Missing validation for domain configurations
## Optimization Proposal
### 1. Enhanced Subdomain Middleware
Create a new, more robust subdomain middleware system:
```php
// app/Http/Middleware/DomainResolver.php
class DomainResolver
{
public function handle($request, Closure $next)
{
$domain = $this->resolveDomain($request);
// Set domain context
app()->instance('domain.context', $domain);
return $next($request);
}
private function resolveDomain($request): DomainContext
{
// Logic to determine domain type and set appropriate context
}
}
```
### 2. Domain Configuration Service
```php
// app/Services/DomainService.php
class DomainService
{
public function getSubdomainType(string $subdomain): string
{
// Determine if subdomain is fixed (my, in, checkout) or dynamic (user shop)
}
public function isValidUserShop(string $slug): bool
{
// Validate user shop existence and status
}
public function getDomainConfiguration(): array
{
// Return centralized domain configuration
}
}
```
### 3. Improved Route Organization
```
routes/
├── web.php # Route registration orchestrator
├── domains/
│ ├── main.php # Main domain (mivita.care)
│ ├── shop.php # Alternative domain (mivita.shop)
│ └── subdomains/
│ ├── crm.php # my.mivita.care
│ ├── portal.php # in.mivita.care
│ ├── checkout.php # checkout.mivita.care
│ └── user-shops.php # {slug}.mivita.care
├── api/
│ └── v1.php # API routes
└── shared/
├── legal.php # Legal pages (shared across domains)
└── common.php # Common functionality
```
### 4. Domain Context System
```php
// app/Domain/DomainContext.php
class DomainContext
{
public function __construct(
public readonly string $type, // 'main', 'crm', 'portal', 'checkout', 'user-shop'
public readonly string $domain, // Full domain
public readonly ?string $subdomain, // Subdomain part
public readonly ?UserShop $userShop // For user shop contexts
) {}
public function isMainDomain(): bool
public function isCrmDomain(): bool
public function isPortalDomain(): bool
public function isCheckoutDomain(): bool
public function isUserShopDomain(): bool
}
```
## Implementation Benefits
### 1. Performance Improvements
- **Reduced Database Queries**: Cache user shop validity
- **Faster Route Resolution**: Dedicated route files per domain type
- **Optimized Middleware Stack**: Domain-specific middleware application
### 2. Maintainability
- **Clear Separation of Concerns**: Each domain type has its own route file
- **Centralized Configuration**: Single source of truth for domain settings
- **Consistent Architecture**: Uniform handling across all domain types
### 3. Scalability
- **Easy Subdomain Addition**: New subdomains can be added without touching existing code
- **Flexible Configuration**: Environment-agnostic domain management
- **Modular Structure**: Independent development of domain-specific features
### 4. Security Enhancements
- **Domain Validation**: Proper validation of all subdomain requests
- **Context Isolation**: Clear boundaries between different application areas
- **CSRF Protection**: Domain-aware CSRF token handling
## Migration Strategy
### Phase 1: Foundation
1. Create new `DomainService` and `DomainContext` classes
2. Implement enhanced `DomainResolver` middleware
3. Add domain configuration management
### Phase 2: Route Reorganization
1. Create new route structure under `routes/domains/`
2. Migrate existing routes to new organization
3. Update route service provider
### Phase 3: Testing & Optimization
1. Comprehensive testing of all domain types
2. Performance optimization and caching
3. Documentation updates
### Phase 4: Deployment
1. Gradual rollout with fallback mechanisms
2. Monitor performance and functionality
3. Remove old code after successful migration
## Risk Assessment
### Low Risk
- Domain service implementation
- Route reorganization
- Configuration centralization
### Medium Risk
- Middleware replacement (affects all requests)
- Session handling changes
- Cache invalidation
### High Risk
- User shop subdomain handling (affects customer shops)
- Payment domain changes (affects revenue)
## Monitoring & Rollback Plan
### Monitoring Points
- Response times per domain type
- Error rates by subdomain
- User shop accessibility
- Payment processing success rates
### Rollback Strategy
- Feature flags for gradual enablement
- Database transaction rollbacks for configuration changes
- Immediate fallback to current middleware if critical issues arise
## Conclusion
This optimization will provide a more maintainable, scalable, and performant multi-domain architecture while preserving all existing functionality. The modular approach allows for incremental implementation and testing, reducing deployment risks.

View file

@ -0,0 +1,344 @@
<?php
namespace App\Providers;
use App\Domain\DomainContext;
use App\Services\DomainService;
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\Facades\Route;
/**
* Enhanced Route Service Provider
*
* This service provider implements the new domain-aware routing system
* that loads routes based on the current domain context.
*/
class RouteServiceProvider extends ServiceProvider
{
/**
* The path to the "home" route for your application.
*/
public const HOME = '/home';
/**
* Define your route model bindings, pattern filters, etc.
*/
public function boot()
{
$this->configureRateLimiting();
$this->routes(function () {
// Load API routes first (domain-independent)
Route::prefix('api')
->middleware('api')
->group(base_path('routes/api.php'));
// Load web routes with domain awareness
Route::middleware('web')
->group(function () {
$this->loadDomainAwareRoutes();
});
});
}
/**
* Load routes based on current domain context
*/
protected function loadDomainAwareRoutes(): void
{
// Try to get domain context (might not be available during route caching)
$domainContext = $this->getDomainContext();
// Load shared routes first (always available)
$this->loadSharedRoutes();
// Load domain-specific routes if context is available
if ($domainContext) {
$this->loadDomainSpecificRoutes($domainContext);
} else {
// Fallback: load all routes for route caching or when context is unavailable
$this->loadAllRoutes();
}
}
/**
* Get current domain context
*/
protected function getDomainContext(): ?DomainContext
{
try {
return app('domain.context');
} catch (\Exception $e) {
// Context not available (e.g., during route caching)
return null;
}
}
/**
* Load shared routes that are available across all domains
*/
protected function loadSharedRoutes(): void
{
// Legal pages (datenschutz, impressum, agb)
Route::group([], base_path('routes/shared/legal.php'));
// Common functionality (health checks, modals, etc.)
Route::group([], base_path('routes/shared/common.php'));
}
/**
* Load routes specific to the current domain
*/
protected function loadDomainSpecificRoutes(DomainContext $context): void
{
match($context->type) {
'main' => $this->loadMainDomainRoutes(),
'main-shop' => $this->loadShopDomainRoutes(),
'crm' => $this->loadCrmDomainRoutes(),
'portal' => $this->loadPortalDomainRoutes(),
'checkout' => $this->loadCheckoutDomainRoutes(),
'user-shop' => $this->loadUserShopDomainRoutes(),
default => null // Unknown domains are handled by middleware
};
}
/**
* Load all routes (fallback for route caching)
*/
protected function loadAllRoutes(): void
{
// Load all domain-specific routes
$this->loadMainDomainRoutes();
$this->loadShopDomainRoutes();
$this->loadCrmDomainRoutes();
$this->loadPortalDomainRoutes();
$this->loadCheckoutDomainRoutes();
$this->loadUserShopDomainRoutes();
}
/**
* Load main domain routes (mivita.care)
*/
protected function loadMainDomainRoutes(): void
{
Route::group([
'middleware' => ['domain.resolver'],
'domain' => $this->getMainDomain(),
], base_path('routes/domains/main.php'));
}
/**
* Load shop domain routes (mivita.shop)
*/
protected function loadShopDomainRoutes(): void
{
Route::group([
'middleware' => ['domain.resolver'],
'domain' => $this->getShopDomain(),
], base_path('routes/domains/shop.php'));
}
/**
* Load CRM domain routes (my.mivita.care)
*/
protected function loadCrmDomainRoutes(): void
{
Route::group([
'middleware' => ['domain.resolver'],
'domain' => $this->getCrmDomain(),
], base_path('routes/domains/subdomains/crm.php'));
}
/**
* Load portal domain routes (in.mivita.care)
*/
protected function loadPortalDomainRoutes(): void
{
Route::group([
'middleware' => ['domain.resolver'],
'domain' => $this->getPortalDomain(),
], base_path('routes/domains/subdomains/portal.php'));
}
/**
* Load checkout domain routes (checkout.mivita.care)
*/
protected function loadCheckoutDomainRoutes(): void
{
Route::group([
'middleware' => ['domain.resolver'],
'domain' => $this->getCheckoutDomain(),
], base_path('routes/domains/subdomains/checkout.php'));
}
/**
* Load user shop domain routes ({slug}.mivita.care)
*/
protected function loadUserShopDomainRoutes(): void
{
Route::group([
'middleware' => ['domain.resolver'],
'domain' => $this->getUserShopDomain(),
], base_path('routes/domains/subdomains/user-shops.php'));
}
/**
* Get main domain pattern
*/
protected function getMainDomain(): string
{
$domain = config('app.domain', 'mivita');
$tld = config('app.tld_care', '.care');
return $domain . $tld;
}
/**
* Get shop domain pattern
*/
protected function getShopDomain(): string
{
$domain = config('app.domain', 'mivita');
$tld = config('app.tld_shop', '.shop');
return $domain . $tld;
}
/**
* Get CRM domain pattern
*/
protected function getCrmDomain(): string
{
$subdomain = rtrim(config('app.pre_url_crm', 'my.'), '.');
$domain = config('app.domain', 'mivita');
$tld = config('app.tld_care', '.care');
return $subdomain . '.' . $domain . $tld;
}
/**
* Get portal domain pattern
*/
protected function getPortalDomain(): string
{
$subdomain = rtrim(config('app.pre_url_portal', 'in.'), '.');
$domain = config('app.domain', 'mivita');
$tld = config('app.tld_care', '.care');
return $subdomain . '.' . $domain . $tld;
}
/**
* Get checkout domain pattern
*/
protected function getCheckoutDomain(): string
{
$subdomain = rtrim(config('app.pre_url_checkout', 'checkout.'), '.');
$domain = config('app.domain', 'mivita');
$tld = config('app.tld_care', '.care');
return $subdomain . '.' . $domain . $tld;
}
/**
* Get user shop domain pattern (wildcard subdomain)
*/
protected function getUserShopDomain(): string
{
$domain = config('app.domain', 'mivita');
$tld = config('app.tld_care', '.care');
return '{subdomain}.' . $domain . $tld;
}
/**
* Configure the rate limiters for the application
*/
protected function configureRateLimiting()
{
// API rate limiting
RateLimiter::for('api', function (Request $request) {
return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
});
// Domain-specific rate limiting
RateLimiter::for('crm', function (Request $request) {
return Limit::perMinute(120)->by($request->user()?->id ?: $request->ip());
});
RateLimiter::for('portal', function (Request $request) {
return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
});
RateLimiter::for('checkout', function (Request $request) {
return Limit::perMinute(30)->by($request->ip());
});
RateLimiter::for('user-shop', function (Request $request) {
return Limit::perMinute(100)->by($request->ip());
});
// Login rate limiting
RateLimiter::for('login', function (Request $request) {
return [
Limit::perMinute(5)->by($request->email . $request->ip()),
Limit::perMinute(10)->by($request->ip()),
];
});
}
/**
* Get route caching path based on domain
*/
public function getCachedRoutesPath()
{
// Use different cache files for different environments
$environment = app()->environment();
return $this->app->bootstrapPath("cache/routes-v7-{$environment}.php");
}
/**
* Determine if routes are cached
*/
public function routesAreCached()
{
return $this->app->routesAreCached();
}
/**
* Load the cached routes for the application
*/
public function loadCachedRoutes()
{
$this->app->booted(function () {
require $this->getCachedRoutesPath();
});
}
/**
* Register domain-specific route macros
*/
protected function registerRouteMacros(): void
{
// Macro for domain-aware redirects
Route::macro('domainRedirect', function (string $domainType, string $path = '/', int $status = 302) {
return function () use ($domainType, $path, $status) {
$domainService = app(DomainService::class);
$url = $domainService->buildUrl($domainType, $path);
return redirect($url, $status);
};
});
// Macro for user shop routes
Route::macro('userShop', function (callable $callback) {
return Route::group([
'middleware' => ['domain.resolver', 'user-shop.validate'],
], $callback);
});
// Macro for CRM routes
Route::macro('crm', function (callable $callback) {
return Route::group([
'middleware' => ['domain.resolver', 'auth'],
'prefix' => '',
], $callback);
});
}
}

View file

@ -0,0 +1,300 @@
# Current Implementation Issues Analysis
## Executive Summary
The current multi-domain and subdomain implementation in the Mivita application has several architectural issues that impact maintainability, performance, and scalability. This document provides a detailed analysis of these issues and their implications.
## 1. Middleware Issues
### 1.1 Subdomain Middleware (`app/Http/Middleware/Subdomain.php`)
#### Critical Issues:
**Hard-coded Fallback Logic**
```php
// Line 47: Hard-coded shop selection
$user_shop = UserShop::where('slug', 'aloevera')->first();
```
- **Impact**: Inflexible fallback mechanism
- **Risk**: Cannot easily change default shop
- **Maintainability**: Low - requires code changes for configuration
**Mixed Responsibilities**
```php
// Lines 24-43: Dynamic subdomain handling
// Lines 44-57: Main domain handling
```
- **Issue**: Single middleware handles multiple domain types
- **Impact**: Complex conditional logic
- **Maintainability**: Difficult to test and modify
**Direct Session Manipulation**
```php
// Lines 39-41: Direct session writes
\Session::put('user_shop', $user_shop);
\Session::put('user_shop_domain', config('app.protocol').$user_shop->slug.".".config('app.domain').config('app.tld_care'));
Config::set('app.url', $user_shop->slug.".".config('app.domain').config('app.tld_care'));
```
- **Issue**: Middleware directly modifies global state
- **Risk**: Side effects and testing difficulties
- **Best Practice**: Middleware should be stateless
**No Error Handling**
- **Issue**: No validation of user shop status
- **Risk**: Invalid shops can cause 503 errors
- **Missing**: Graceful degradation
### 1.2 Missing Validation
**User Shop Validation Issues**:
```php
// Lines 30-38: Validation logic
if(!$user_shop->active){
abort(503);
}
if(!$user_shop->user){
abort(503);
}
if(!$user_shop->user->isActiveShop()){
abort(503);
}
```
- **Issue**: Returns 503 (Service Unavailable) for invalid shops
- **Better**: Should return 404 or redirect to main domain
- **SEO Impact**: 503 errors can negatively affect search rankings
## 2. Routing Architecture Issues
### 2.1 Route File Organization
Current structure:
```
routes/
├── web.php (mostly empty)
├── main.php
├── subdomain.php
├── crm.php
├── portal.php
├── checkout.php
├── api.php
└── utility.php
```
#### Issues:
**Route Duplication**
- Legal routes (`/datenschutz`, `/impressum`, `/agb`) duplicated across multiple files
- Contact routes duplicated
- Registration routes duplicated
**Inconsistent Middleware Application**
```php
// crm.php - Line 12: Domain-based grouping
Route::domain(config('app.pre_url_crm') . config('app.domain') . config('app.tld_care'))->group(function () {
// subdomain.php - Line 10: Middleware-based grouping
Route::group(['middleware' => ['subdomain']], function () {
```
**Complex Domain Logic in Routes**
- Domain configuration scattered across route files
- Hard to understand which routes belong to which domain
- Difficult to add new domain types
### 2.2 Route Registration Issues
**Missing Route Prefixes**
- No clear namespacing for different domain types
- Route name conflicts possible
- Difficult to generate domain-specific URLs
**Inefficient Route Loading**
- All routes loaded regardless of current domain
- Impacts performance for large applications
- Unnecessary route compilation
## 3. Configuration Management Issues
### 3.1 Environment Configuration (`.env`)
Current configuration:
```env
APP_DOMAIN=mivita
APP_TLD_CARE=.test
APP_TLD_SHOP=.lshop
APP_URL_CHECKOUT=checkout.
APP_URL_CRM=my.
APP_URL_PORTAL=in.
```
#### Issues:
**Inconsistent Naming**
- `APP_TLD_CARE` vs `APP_TLD_SHOP` - inconsistent naming pattern
- `APP_URL_*` contains trailing dots - configuration inconsistency
**Missing Validation**
- No validation of domain configuration
- Invalid configurations can cause runtime errors
- No documentation of required format
**Environment Dependency**
- Different TLDs for different environments
- Configuration changes required for different deployments
- No centralized domain management
### 3.2 Runtime Configuration Issues
**Dynamic URL Setting**
```php
// Subdomain.php - Line 41
Config::set('app.url', $user_shop->slug.".".config('app.domain').config('app.tld_care'));
```
- **Issue**: Runtime modification of application URL
- **Risk**: Affects URL generation throughout application
- **Problem**: Can cause inconsistent URLs in different parts of application
## 4. Performance Issues
### 4.1 Database Queries
**No Caching**
```php
// Line 26: Database query on every request
$user_shop = UserShop::where('slug', $request->route('subdomain'))->first();
```
- **Impact**: Database query for every subdomain request
- **Scale**: Significant load with many user shops
- **Solution**: Implement caching strategy
**N+1 Query Potential**
```php
// Lines 33-37: Potential additional queries
if(!$user_shop->user){
abort(503);
}
if(!$user_shop->user->isActiveShop()){
abort(503);
}
```
- **Issue**: Multiple database queries per request
- **Impact**: Poor performance with many concurrent requests
### 4.2 Route Compilation
**All Routes Loaded**
- Every request loads all route files
- No domain-specific route caching
- Impacts application bootstrap time
## 5. Security Issues
### 5.1 Session Management
**Inconsistent Session Domains**
```php
// .env - Line 26
SESSION_DOMAIN=.mivita.test
```
- **Issue**: Fixed session domain across all subdomains
- **Risk**: Session sharing between unrelated domains
- **Security**: Potential session hijacking between user shops
### 5.2 CSRF Protection
**Missing Domain-Specific CSRF**
- No domain-specific CSRF token handling
- Potential cross-domain CSRF issues
- Missing validation for domain-specific requests
## 6. Maintainability Issues
### 6.1 Code Organization
**Scattered Domain Logic**
- Domain handling logic in multiple files
- No single source of truth for domain configuration
- Difficult to understand complete domain architecture
**Missing Abstractions**
- No domain context object
- Direct use of request/session data
- Tight coupling between components
### 6.2 Testing Challenges
**Difficult to Test**
- Middleware has side effects
- Global state modifications
- Complex conditional logic
**Missing Test Coverage**
- No unit tests for domain logic
- Integration tests difficult to write
- Manual testing required for each domain type
## 7. Scalability Issues
### 7.1 Adding New Domains
**Hard to Extend**
- Adding new subdomain types requires multiple file changes
- No consistent pattern for new domain types
- Complex configuration requirements
### 7.2 Multi-tenant Considerations
**Poor Tenant Isolation**
- User shops not properly isolated
- Shared configuration between tenants
- Potential data leakage between shops
## 8. Documentation Issues
### 8.1 Missing Documentation
**No Architecture Documentation**
- Domain structure not documented
- Routing logic not explained
- Configuration options not documented
**No Deployment Guide**
- Missing deployment instructions
- No environment-specific guidance
- No troubleshooting documentation
## Impact Assessment
### High Impact Issues
1. **Performance**: Database queries on every request
2. **Security**: Session domain configuration issues
3. **Maintainability**: Scattered domain logic
### Medium Impact Issues
1. **Route duplication**: Maintenance overhead
2. **Configuration management**: Deployment complexity
3. **Error handling**: Poor user experience
### Low Impact Issues
1. **Code organization**: Developer productivity
2. **Documentation**: Onboarding difficulty
3. **Testing**: Quality assurance challenges
## Recommendations Priority
### Priority 1 (Critical)
1. Implement caching for user shop lookups
2. Fix session domain configuration
3. Improve error handling for invalid shops
### Priority 2 (High)
1. Refactor middleware architecture
2. Reorganize route structure
3. Centralize domain configuration
### Priority 3 (Medium)
1. Add comprehensive testing
2. Create documentation
3. Implement monitoring
This analysis provides the foundation for the optimization proposal detailed in the main README.md file.

View file

@ -0,0 +1,435 @@
# Implementation Guide - Mivita Subdomain Optimization
## Overview
This guide provides step-by-step instructions for implementing the optimized multi-domain and subdomain architecture for the Mivita application.
## Prerequisites
- Backup of current system
- Test environment available
- Access to web server configuration
- Understanding of Laravel routing and middleware
## Phase 1: Foundation Setup
### Step 1: Create New Service Classes
1. **Create DomainService**
```bash
# Copy from dev/subdomain-optimization/DomainService.php
cp dev/subdomain-optimization/DomainService.php app/Services/DomainService.php
```
2. **Create DomainContext**
```bash
# Create Domain directory and copy context
mkdir -p app/Domain
cp dev/subdomain-optimization/DomainContext.php app/Domain/DomainContext.php
```
3. **Create New Middleware**
```bash
# Copy enhanced middleware
cp dev/subdomain-optimization/DomainResolver.php app/Http/Middleware/DomainResolver.php
```
### Step 2: Register New Services
Add to `config/app.php`:
```php
'providers' => [
// ... existing providers
App\Providers\DomainServiceProvider::class,
],
```
Create `app/Providers/DomainServiceProvider.php`:
```php
<?php
namespace App\Providers;
use App\Services\DomainService;
use Illuminate\Support\ServiceProvider;
class DomainServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->singleton(DomainService::class);
}
public function boot()
{
//
}
}
```
### Step 3: Update Kernel Configuration
Add new middleware to `app/Http/Kernel.php`:
```php
protected $middleware = [
// ... existing middleware
\App\Http\Middleware\DomainResolver::class,
];
protected $middlewareGroups = [
'web' => [
// Remove old Subdomain middleware
// \App\Http\Middleware\Subdomain::class,
// ... other middleware
],
];
```
## Phase 2: Route Structure Migration
### Step 1: Create New Route Structure
```bash
# Create new directory structure
mkdir -p routes/domains/subdomains
mkdir -p routes/shared/crm
mkdir -p routes/shared/api
# Create placeholder files
touch routes/domains/main.php
touch routes/domains/shop.php
touch routes/domains/subdomains/crm.php
touch routes/domains/subdomains/portal.php
touch routes/domains/subdomains/checkout.php
touch routes/domains/subdomains/user-shops.php
touch routes/shared/legal.php
touch routes/shared/common.php
```
### Step 2: Migrate Existing Routes
1. **Legal Routes** (move to `routes/shared/legal.php`)
- Extract legal routes from all current route files
- Remove duplicates
- Standardize naming
2. **Main Domain Routes** (move to `routes/domains/main.php`)
- Extract routes from `routes/main.php`
- Clean up and organize
3. **CRM Routes** (move to `routes/domains/subdomains/crm.php`)
- Extract routes from `routes/crm.php`
- Split into sub-files for organization
4. **User Shop Routes** (move to `routes/domains/subdomains/user-shops.php`)
- Extract routes from `routes/subdomain.php`
- Remove domain-specific logic (handled by middleware)
### Step 3: Update Route Service Provider
Replace `app/Providers/RouteServiceProvider.php` with the optimized version:
```bash
cp dev/subdomain-optimization/RouteServiceProvider.php app/Providers/RouteServiceProvider.php
```
## Phase 3: Testing and Validation
### Step 1: Unit Tests
Create tests for new components:
```bash
php artisan make:test DomainServiceTest --unit
php artisan make:test DomainContextTest --unit
php artisan make:test DomainResolverTest
```
### Step 2: Feature Tests
Test each domain type:
```bash
php artisan make:test MainDomainTest
php artisan make:test CrmDomainTest
php artisan make:test UserShopDomainTest
php artisan make:test CheckoutDomainTest
php artisan make:test PortalDomainTest
```
### Step 3: Manual Testing
Test each domain type manually:
1. **Main Domain** (`mivita.care`)
- Homepage loads correctly
- Registration works
- Contact forms work
- Legal pages accessible
2. **CRM Domain** (`my.mivita.care`)
- Login system works
- User dashboard accessible
- Admin functions work
- Session management correct
3. **User Shops** (`{slug}.mivita.care`)
- Valid shops load correctly
- Invalid shops redirect properly
- Shopping cart functionality works
- Session isolation working
4. **Checkout Domain** (`checkout.mivita.care`)
- Payment processing works
- Transaction handling correct
- Security measures in place
5. **Portal Domain** (`in.mivita.care`)
- Customer login works
- Portal functionality accessible
- Data isolation correct
## Phase 4: Performance Optimization
### Step 1: Enable Caching
1. **Route Caching**
```bash
php artisan route:cache
```
2. **User Shop Caching**
- Configure Redis/Memcached
- Test cache invalidation
- Monitor cache hit rates
### Step 2: Database Optimization
1. **Add Indexes**
```sql
CREATE INDEX idx_user_shops_slug_active ON user_shops (slug, active);
CREATE INDEX idx_users_shop_active ON users (id) WHERE shop_active = 1;
```
2. **Query Optimization**
- Review N+1 queries
- Optimize user shop lookups
- Add eager loading where needed
### Step 3: Monitoring Setup
1. **Application Monitoring**
- Add performance metrics
- Monitor response times per domain
- Track error rates by domain type
2. **Cache Monitoring**
- Monitor cache hit/miss rates
- Track cache memory usage
- Set up cache warming
## Phase 5: Deployment Strategy
### Step 1: Feature Flags
Implement feature flags for gradual rollout:
```php
// In DomainResolver middleware
if (config('features.new_domain_resolver', false)) {
// Use new logic
} else {
// Fall back to old middleware
}
```
### Step 2: Blue-Green Deployment
1. **Prepare Blue Environment**
- Deploy new code to blue environment
- Test all functionality
- Verify performance metrics
2. **Switch Traffic**
- Gradually route traffic to blue
- Monitor for issues
- Ready to rollback if needed
### Step 3: Monitoring During Deployment
1. **Real-time Monitoring**
- Response times
- Error rates
- User shop accessibility
- Payment processing success
2. **Rollback Triggers**
- Error rate > 1%
- Response time > 2x baseline
- Payment failures > 0.1%
- User complaints
## Rollback Plan
### Immediate Rollback (< 5 minutes)
1. **Revert Middleware**
```php
// In Kernel.php, re-enable old middleware
\App\Http\Middleware\Subdomain::class,
```
2. **Revert Route Service Provider**
```bash
git checkout HEAD~1 app/Providers/RouteServiceProvider.php
```
3. **Clear Caches**
```bash
php artisan route:clear
php artisan config:clear
php artisan cache:clear
```
### Full Rollback (< 30 minutes)
1. **Revert All Changes**
```bash
git revert <commit-hash>
```
2. **Redeploy Previous Version**
```bash
# Deploy previous known-good version
```
3. **Verify Functionality**
- Test all domain types
- Verify user shop access
- Check payment processing
## Configuration Management
### Environment Variables
Add to `.env`:
```env
# Domain optimization feature flags
DOMAIN_RESOLVER_ENABLED=true
DOMAIN_CACHE_ENABLED=true
DOMAIN_CACHE_TTL=3600
# Monitoring
DOMAIN_MONITORING_ENABLED=true
DOMAIN_PERFORMANCE_TRACKING=true
```
### Configuration Files
Update `config/domains.php`:
```php
<?php
return [
'cache_ttl' => env('DOMAIN_CACHE_TTL', 3600),
'monitoring_enabled' => env('DOMAIN_MONITORING_ENABLED', false),
'performance_tracking' => env('DOMAIN_PERFORMANCE_TRACKING', false),
'fixed_subdomains' => ['my', 'in', 'checkout'],
'middleware_stacks' => [
'main' => ['web'],
'crm' => ['web', 'auth'],
'portal' => ['web', 'auth:customers'],
'checkout' => ['web', 'checkout'],
'user-shop' => ['web', 'subdomain'],
],
];
```
## Maintenance Procedures
### Regular Maintenance
1. **Weekly Cache Clearing**
```bash
# Clear expired user shop cache entries
php artisan domain:cache:clean
```
2. **Monthly Performance Review**
- Review response time metrics
- Check cache hit rates
- Analyze error patterns
3. **Quarterly Domain Audit**
- Review user shop validity
- Clean up inactive shops
- Update domain configurations
### Emergency Procedures
1. **User Shop Outage**
- Identify affected shops
- Clear specific cache entries
- Notify affected users
2. **Domain Resolution Issues**
- Check DNS configuration
- Verify middleware configuration
- Test domain parsing logic
3. **Performance Degradation**
- Check cache status
- Review database performance
- Scale resources if needed
## Success Metrics
### Performance Metrics
- **Response Time**: < 200ms for cached requests
- **Cache Hit Rate**: > 95% for user shop lookups
- **Error Rate**: < 0.1% across all domains
- **Uptime**: > 99.9% per domain type
### Business Metrics
- **User Shop Accessibility**: 100% for active shops
- **Payment Success Rate**: > 99.5%
- **Customer Satisfaction**: No complaints about domain issues
- **Development Velocity**: Faster feature development per domain
### Technical Metrics
- **Code Maintainability**: Reduced cyclomatic complexity
- **Test Coverage**: > 90% for domain-related code
- **Documentation**: All new components documented
- **Security**: No domain-based security vulnerabilities

View file

@ -0,0 +1,321 @@
# Optimized Route Structure
## Current Route Organization Issues
The current routing system has several problems:
- Route duplication across multiple files
- Complex domain-based routing spread across different files
- Inconsistent middleware application
- Hard to maintain and understand
## Proposed New Structure
```
routes/
├── web.php # Main route orchestrator
├── api.php # API routes (unchanged)
├── console.php # Console routes (unchanged)
├── channels.php # Broadcasting routes (unchanged)
├── domains/
│ ├── main.php # Main domain routes (mivita.care)
│ ├── shop.php # Shop domain routes (mivita.shop)
│ └── subdomains/
│ ├── crm.php # CRM routes (my.mivita.care)
│ ├── portal.php # Portal routes (in.mivita.care)
│ ├── checkout.php # Checkout routes (checkout.mivita.care)
│ └── user-shops.php # User shop routes ({slug}.mivita.care)
└── shared/
├── legal.php # Legal pages (shared across domains)
├── common.php # Common functionality
└── api/
└── v1.php # API version 1 routes
```
## Implementation Examples
### routes/web.php (Orchestrator)
```php
<?php
use App\Domain\DomainContext;
use Illuminate\Support\Facades\Route;
/*
|--------------------------------------------------------------------------
| Web Routes Orchestrator
|--------------------------------------------------------------------------
|
| This file serves as the main orchestrator for all web routes.
| Domain-specific routes are loaded based on the current domain context.
|
*/
// Get domain context from middleware
$domainContext = app('domain.context');
// Load shared routes first (legal pages, etc.)
require __DIR__ . '/shared/legal.php';
require __DIR__ . '/shared/common.php';
// Load domain-specific routes based on context
match($domainContext->type) {
'main' => require __DIR__ . '/domains/main.php',
'main-shop' => require __DIR__ . '/domains/shop.php',
'crm' => require __DIR__ . '/domains/subdomains/crm.php',
'portal' => require __DIR__ . '/domains/subdomains/portal.php',
'checkout' => require __DIR__ . '/domains/subdomains/checkout.php',
'user-shop' => require __DIR__ . '/domains/subdomains/user-shops.php',
default => null // Unknown domains handled by middleware
};
```
### routes/domains/main.php (Main Domain)
```php
<?php
use Illuminate\Support\Facades\Route;
/*
|--------------------------------------------------------------------------
| Main Domain Routes (mivita.care)
|--------------------------------------------------------------------------
*/
Route::middleware(['domain.resolver'])->group(function () {
// Home page
Route::get('/', 'Web\SiteController@index')->name('home');
// Registration
Route::get('/registrierung', 'Web\RegisterController@index')->name('register_user');
Route::get('/reg/{member_id?}', 'Web\RegisterController@member')->name('register_user_member');
Route::post('/registrierung', 'Web\RegisterController@register')->name('register_user');
Route::get('/registrierung/finish', 'Web\RegisterController@finish')->name('register_user_finish');
// Contact
Route::get('/kontakt', 'Web\ContactController@create')->name('contact_create');
Route::post('/kontakt', 'Web\ContactController@store')->name('contact_store');
// Dynamic site routing
Route::get('/{site}/{subsite?}/{product_slug?}', 'Web\SiteController@site')->name('base.site');
// Language switching
Route::post('/change_website_lang', 'Web\SiteController@changeLang')->name('change_website_lang');
});
```
### routes/domains/subdomains/crm.php (CRM Domain)
```php
<?php
use Illuminate\Support\Facades\Route;
/*
|--------------------------------------------------------------------------
| CRM Domain Routes (my.mivita.care)
|--------------------------------------------------------------------------
*/
Route::domain(config('app.pre_url_crm') . config('app.domain') . config('app.tld_care'))
->middleware(['domain.resolver', 'web'])
->group(function () {
// Cron jobs (public access)
Route::get('/cron/jobs/action/{action}/{key}', 'CronController@action')->name('cron_jobs_action');
Route::get('/cron/jobs/run/{key}', 'CronController@runCron')->name('cron_jobs_run');
// Authentication routes
Auth::routes();
Route::get('/logout', function () {
Auth::logout();
return redirect()->route('login');
})->name('logout');
// Public routes
Route::get('/', 'HomeController@index')->name('home');
Route::get('/register/verify/{confirmationCode}', 'HomeController@verify')->name('register_verify');
Route::get('/homeparty/{token?}/{gid?}', 'Web\HomepartyController@detail')->name('homeparty');
Route::post('/homeparty/{token?}/{gid?}', 'Web\HomepartyController@detailStore')->name('homeparty');
// Authenticated routes
Route::middleware(['auth'])->group(function () {
require __DIR__ . '/../../shared/crm/authenticated.php';
});
// Admin routes
Route::middleware(['admin'])->group(function () {
require __DIR__ . '/../../shared/crm/admin.php';
});
// Super admin routes
Route::middleware(['superadmin'])->group(function () {
require __DIR__ . '/../../shared/crm/superadmin.php';
});
// System admin routes
Route::middleware(['sysadmin'])->group(function () {
require __DIR__ . '/../../shared/crm/sysadmin.php';
});
});
```
### routes/domains/subdomains/user-shops.php (User Shops)
```php
<?php
use Illuminate\Support\Facades\Route;
/*
|--------------------------------------------------------------------------
| User Shop Domain Routes ({slug}.mivita.care)
|--------------------------------------------------------------------------
*/
Route::middleware(['domain.resolver', 'web'])->group(function () {
// Home page
Route::get('/', 'Web\SiteController@index')->name('shop.home');
// Shopping cart functionality
Route::prefix('user/card')->name('user.card_')->group(function () {
Route::get('/add/{id}/{quantity?}/{product_slug?}', 'Web\CardController@addToCardGet')->name('add_get');
Route::post('/add/{id}', 'Web\CardController@addToCardPost')->name('add_post');
Route::get('/show', 'Web\CardController@showCard')->name('show');
Route::get('/checkout/server', 'Web\CardController@checkoutServer')->name('checkout_server');
Route::post('/update', 'Web\CardController@updateCard')->name('update');
Route::get('/remove/{rowId}', 'Web\CardController@removeCard')->name('remove');
Route::get('/delete', 'Web\CardController@deleteCard')->name('delete');
});
// Shop navigation
Route::get('/user/back/to/shop/{reference?}', 'Web\CardController@backToShop')->name('user.back_to_shop');
Route::get('/domain/check', 'Web\SiteController@domainCheck')->name('user.domain_check');
// Registration with referral
Route::get('/registrierung', 'Web\RegisterController@index')->name('shop.register_user');
Route::get('/reg/{member_id?}', 'Web\RegisterController@member')->name('shop.register_user_member');
Route::post('/registrierung', 'Web\RegisterController@register')->name('shop.register_user');
Route::get('/registrierung/finish', 'Web\RegisterController@finish')->name('shop.register_user_finish');
// Dynamic site routing (must be last)
Route::get('/{site}/{subsite?}/{product_slug?}', 'Web\SiteController@site')->name('shop.site');
// Language switching
Route::post('/change_website_lang', 'Web\SiteController@changeLang')->name('shop.change_website_lang');
});
```
### routes/shared/legal.php (Shared Legal Pages)
```php
<?php
use Illuminate\Support\Facades\Route;
/*
|--------------------------------------------------------------------------
| Shared Legal Routes
|--------------------------------------------------------------------------
| These routes are available across all domain types
*/
Route::get('/datenschutz', 'HomeController@legalDataProtected')->name('datenschutz');
Route::get('/impressum', 'HomeController@legalImprint')->name('impressum');
Route::get('/agb', 'HomeController@legalAGB')->name('agb');
// English routes
Route::get('/data-protection', 'HomeController@legalDataProtected')->name('data_protected');
Route::get('/imprint', 'HomeController@legalImprint')->name('imprint');
Route::get('/terms', 'HomeController@legalAGB')->name('terms');
```
### routes/shared/common.php (Common Functionality)
```php
<?php
use Illuminate\Support\Facades\Route;
/*
|--------------------------------------------------------------------------
| Common Routes
|--------------------------------------------------------------------------
| These routes provide common functionality across domains
*/
// Modal loading (AJAX)
Route::post('/loading/modal', 'HomeController@loadingModal')->name('loading_modal');
// Health check
Route::get('/health', function () {
return response()->json(['status' => 'ok']);
})->name('health_check');
// Maintenance mode check
Route::get('/maintenance', function () {
return view('maintenance');
})->name('maintenance');
```
## Benefits of New Structure
### 1. Clear Separation of Concerns
- Each domain type has its own route file
- Shared functionality is clearly separated
- Easy to understand which routes belong to which domain
### 2. Reduced Duplication
- Legal pages defined once and shared
- Common functionality centralized
- Domain-specific routes only defined once
### 3. Better Maintainability
- Changes to specific domain types are isolated
- Easier to add new domain types
- Clear structure for new developers
### 4. Performance Benefits
- Only relevant routes are loaded per domain
- Reduced route compilation time
- Better caching possibilities
### 5. Enhanced Security
- Domain-specific middleware applied correctly
- Easier to implement domain-specific security rules
- Clear boundaries between different application areas
## Migration Strategy
### Phase 1: Create New Structure
1. Create new directory structure
2. Copy existing routes to appropriate new files
3. Test each domain type individually
### Phase 2: Update Route Service Provider
1. Modify RouteServiceProvider to use new orchestrator
2. Implement domain context checking
3. Add fallback mechanisms
### Phase 3: Clean Up
1. Remove old route files
2. Update any hardcoded route references
3. Update documentation
### Phase 4: Optimize
1. Implement route caching per domain
2. Add performance monitoring
3. Optimize middleware stack per domain type