Architecture
Architecture decisions
Key high-level choices are captured in:
System Architecture
Wedissimo API is built as a modular monolith, combining the organizational benefits of microservices with the simplicity of a monolithic application.
Core Principles
1. Modular Monolith Pattern
Each feature is isolated into a module with its own:
- Controllers and routes (API and web)
- Models and migrations
- Services and business logic
- Livewire components and views
- Permissions and navigation
- Tests and documentation
Module Structure:
modules/DomainName/
├── Config/ # Module configuration
├── Database/
│ ├── Migrations/ # Domain-specific migrations
│ └── Seeders/ # Permission seeders
├── Http/
│ ├── Controllers/ # API controllers
│ └── Resources/ # API resources
├── Livewire/ # Livewire components
├── Models/ # Domain models
├── Providers/ # ServiceProvider
├── Resources/views/ # Blade views
│ ├── livewire/ # Component views
│ └── partials/nav.blade.php # Admin navigation
├── Routes/
│ ├── api.php # API routes (v1)
│ └── web.php # Web routes (Livewire)
└── Tests/ # Domain testsModule Communication:
- Eloquent relationships (cross-domain)
- Service contracts (interfaces)
- Laravel events (loosely coupled)
- Direct service injection (when appropriate)
Key Modules:
- Vendor (
modules/Vendor/) - Vendor management and search - MagicLink (
modules/MagicLink/) - Passwordless authentication - UserInvitation (
modules/UserInvitation/) - User invitation system - Profile (
modules/Profile/) - User profile management
See Module System Guide and Domain Documentation for details.
2. Service Layer Architecture
Request → Controller → Service → Model → Database
↓
API Resource → ResponseControllers handle HTTP concerns Services contain business logic Models manage data persistence Resources format API responses
3. UUID Primary Keys
All models use UUID primary keys for:
- Security (non-sequential IDs)
- Distributed system compatibility
- Migration from WordPress
use Illuminate\Database\Eloquent\Concerns\HasUuids;
class YourModel extends Model
{
use HasUuids;
}Database Architecture
Multi-Database Setup
Primary (PostgreSQL)
- Connection:
pgsql - Purpose: All application data
- Features: UUID support, full-text search, JSONB
Legacy (MySQL)
- Connection:
mysql_legacy - Purpose: WordPress migration source
- Package: Corcel for WordPress ORM
- Pattern: Maintains
wp_idforeign keys
Typesense (Search)
- Connection: HTTP API
- Purpose: Full-text search engine
- Integration: Laravel Scout
- Port: 8108 (internal), 1026 (local)
Migration Patterns
Always include all column attributes when modifying:
// WRONG - drops attributes
$table->string('password')->nullable()->change();
// CORRECT - preserves attributes
$table->string('password', 255)->nullable()->change();Authentication Architecture
Architecture decisions
Multi-Strategy Authentication
┌─────────────┐
│ Request │
└──────┬──────┘
│
├──────────────────────────────┐
│ │
┌──────▼────────┐ ┌────────▼────────┐
│ Session Auth │ │ Sanctum Token │
│ (web guard) │ │ (sanctum guard)│
└──────┬────────┘ └────────┬────────┘
│ │
└──────────┬───────────────────┘
│
┌──────▼─────┐
│ Magic Link │
│ (signed) │
└────────────┘User Roles (Spatie Permission)
super_admin- Full system accesswedissimo_admin- Platform administrationvendor- Service providercouple- Wedding coupleuser- Basic authenticated user
Search Architecture
Scout + Typesense Integration
Model Change → Observer → Queue Job → Scout → Typesense
│
User Search ← Controller ← Scout ← ← ← ← ← ← ← ←─┘Searchable Models:
App\Models\Listing- Service listings (core)Modules\Vendor\Models\Vendor- Vendor profiles (module)App\Models\User- User search (core)Modules\UserInvitation\Models\UserInvitation- Invitations (module)
Features:
- Auto-sync on model changes
- Queued index operations
- Typo tolerance
- Geo-location search
- Faceted filtering
Admin Search Pages:
/admin/search/listings- Livewire listing search/admin/search/vendors- Livewire vendor search (module-based)
See Search Documentation for details.
Media Architecture
Architecture decision
The rationale for using a shared polymorphic media system with unified collection names is described in ADR 0002: Polymorphic media with unified collection names.
Polymorphic Media System
// Central Media model
Media::class
// Polymorphic pivot
Mediable::class (media_id, mediable_id, mediable_type, collection, order)
// In any model
public function media()
{
return $this->morphToMany(Media::class, 'mediable')
->withPivot('collection', 'order')
->orderBy('order');
}Collections: avatar, gallery, documents, etc.
API Architecture
Versioning Strategy
Current version: v1
/api/v1/users
/api/v1/vendors
/api/v1/listingsResponse Format
Always use Eloquent API Resources:
// Single resource
return new UserResource($user);
// Collection
return UserResource::collection($users);
// Paginated
return UserResource::collection(
User::paginate(15)
);Error Handling
Centralized in app/Exceptions/Handler.php:
- API errors return JSON
- Validation errors formatted consistently
- Bugsnag integration for production
Event-Driven Architecture
Key Events
// User events
UserCreated::class
UserUpdated::class
// Vendor events
VendorApproved::class
VendorRejected::class
// Listing events
ListingPublished::class
ListingUnpublished::classListeners Pattern
// Sync listeners (immediate)
SendWelcomeEmail::class
// Queued listeners (background)
SyncToStripe::class
UpdateSearchIndex::classCaching Strategy
Cache Layers
Application Cache (Redis)
- Session storage
- Queue jobs
- Rate limiting
Model Cache (Query results)
- Cached eloquent queries
- Remember pattern
HTTP Cache (Response caching)
- API responses
- Static content
LaRecipe Cache (Documentation)
- 60-minute cache
- Markdown rendering
Queue Architecture
Queue Workers
# Development
docker exec wedissimo-api php artisan queue:work
# Production (Supervisor)
[program:wedissimo-worker]
command=php artisan queue:workJob Types
- High Priority: Emails, notifications
- Default: Search indexing, webhooks
- Low Priority: Analytics, cleanups
Testing Architecture
Test Types
tests/
├── Feature/ # HTTP requests, DB interactions
├── Unit/ # Isolated logic, services
└── Pest.php # Shared setup, helpersCoverage Strategy
- Controllers: Request/response validation
- Services: Business logic coverage
- Models: Relationships, scopes
- Jobs: Queue processing
Target: 80% minimum coverage
Deployment Architecture
Docker Services
wedissimo-api: # Laravel app
wedissimo-pg: # PostgreSQL
wedissimo-typesense: # Search engine
wedissimo-mailpit: # Email testing
wedissimo-mysql: # Legacy dataEnvironment Modes
- Local: Docker Compose
- Staging: Cloud Run + Cloud SQL
- Production: Cloud Run + Cloud SQL + CDN
Performance Optimization
Database
- Indexes on foreign keys
- Composite indexes for common queries
- Query optimization via Telescope
Search
- Queued sync operations
- Cached search results
- Geo-location indexing
Assets
- Vite for bundling
- CDN for static files
- Image optimization (Intervention)
Caching
- Query result caching
- Route caching (production)
- Config caching (production)
Security
Best Practices
- Environment variables for secrets
- CSRF protection (web routes)
- Rate limiting (API routes)
- SQL injection prevention (Eloquent)
- XSS protection (Blade escaping)
- Mass assignment protection
Authentication
- Password hashing (bcrypt)
- Token-based API auth
- Signed URLs for magic links
- Role-based permissions