Architecture Overview
Comprehensive guide to LaraDashboard's architecture, design patterns, and system components for developers building modules and extensions.
Architecture Overview
LaraDashboard follows a clean, modular architecture built on Laravel's best practices. Understanding this architecture is essential for developers who want to extend, customize, or build modules for the platform.
High-Level Architecture
┌─────────────────────────────────────────────────────────────────┐
│ Presentation Layer │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────┐ │
│ │ Livewire │ │ Blade │ │ React Components │ │
│ │ Components │ │ Views │ │ (LaraBuilder) │ │
│ └──────────────┘ └──────────────┘ └──────────────────────┘ │
├─────────────────────────────────────────────────────────────────┤
│ Application Layer │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────┐ │
│ │ Controllers │ │ Form │ │ API Resources │ │
│ │ │ │ Requests │ │ │ │
│ └──────────────┘ └──────────────┘ └──────────────────────┘ │
├─────────────────────────────────────────────────────────────────┤
│ Service Layer │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────┐ │
│ │ Services │ │ Registries │ │ Hooks System │ │
│ │ │ │ │ │ │ │
│ └──────────────┘ └──────────────┘ └──────────────────────┘ │
├─────────────────────────────────────────────────────────────────┤
│ Domain Layer │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────┐ │
│ │ Models │ │ Policies │ │ Observers │ │
│ │ │ │ │ │ │ │
│ └──────────────┘ └──────────────┘ └──────────────────────┘ │
├─────────────────────────────────────────────────────────────────┤
│ Infrastructure Layer │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────┐ │
│ │ Database │ │ Cache │ │ File Storage │ │
│ │ │ │ │ │ │ │
│ └──────────────┘ └──────────────┘ └──────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
Design Patterns
LaraDashboard implements several design patterns to maintain clean, extensible code:
Service Pattern
Business logic is encapsulated in service classes:
namespace App\Services;
class PostService
{
public function __construct(
private SlugService $slugService,
private CacheService $cacheService
) {}
public function create(array $data): Post
{
$data['slug'] = $this->slugService->generateUnique(
$data['title'],
Post::class
);
$post = Post::create($data);
$this->cacheService->forget('posts.recent');
return $post;
}
public function update(Post $post, array $data): Post
{
$post->update($data);
$this->cacheService->forget("post.{$post->id}");
return $post->fresh();
}
}
Registry Pattern
Extensible systems use registries for dynamic registration:
namespace App\Services;
class EmailProviderRegistry
{
protected array $providers = [];
public function register(string $key, string $class): void
{
$this->providers[$key] = $class;
}
public function get(string $key): ?EmailProviderInterface
{
if (!isset($this->providers[$key])) {
return null;
}
return app($this->providers[$key]);
}
public function all(): array
{
return $this->providers;
}
}
Observer Pattern
Model events trigger observers for side effects:
namespace App\Observers;
class PostObserver
{
public function created(Post $post): void
{
ActionLog::create([
'user_id' => auth()->id(),
'action' => 'created',
'model_type' => Post::class,
'model_id' => $post->id,
]);
}
public function updated(Post $post): void
{
Cache::forget("post.{$post->id}");
}
public function deleted(Post $post): void
{
$post->meta()->delete();
$post->clearMediaCollection('featured');
}
}
Policy Pattern
Authorization logic lives in policy classes:
namespace App\Policies;
class PostPolicy
{
public function viewAny(User $user): bool
{
return $user->can('posts.view');
}
public function create(User $user): bool
{
return $user->can('posts.create');
}
public function update(User $user, Post $post): bool
{
if ($user->can('posts.edit')) {
return true;
}
return $user->id === $post->user_id
&& $user->can('posts.edit_own');
}
public function delete(User $user, Post $post): bool
{
return $user->can('posts.delete');
}
}
Request Lifecycle
Understanding the request lifecycle helps when debugging or extending:
┌──────────────────────────────────────────────────────────────────┐
│ HTTP Request │
└──────────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────┐
│ 1. Middleware Pipeline │
│ - Authentication │
│ - Authorization │
│ - Rate Limiting │
│ - CSRF Protection │
└──────────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────┐
│ 2. Route Resolution │
│ - Match route pattern │
│ - Apply route middleware │
│ - Resolve controller/action │
└──────────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────┐
│ 3. Form Request Validation │
│ - Validate input data │
│ - Authorize action │
│ - Sanitize inputs │
└──────────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────┐
│ 4. Controller Action │
│ - Invoke service layer │
│ - Process business logic │
│ - Return response │
└──────────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────┐
│ 5. Response │
│ - Render view (Blade/Livewire) │
│ - Return JSON (API) │
│ - Redirect │
└──────────────────────────────────────────────────────────────────┘
Core Components
Controllers
Controllers handle HTTP requests and delegate to services:
namespace App\Http\Controllers\Backend;
class PostController extends Controller
{
public function __construct(
private PostService $postService
) {}
public function store(StorePostRequest $request)
{
$post = $this->postService->create($request->validated());
return redirect()
->route('admin.posts.edit', $post)
->with('success', 'Post created successfully');
}
}
Form Requests
Validation and authorization in dedicated classes:
namespace App\Http\Requests\Post;
class StorePostRequest extends FormRequest
{
public function authorize(): bool
{
return $this->user()->can('posts.create');
}
public function rules(): array
{
return [
'title' => ['required', 'string', 'max:255'],
'content' => ['required', 'string'],
'status' => ['required', Rule::in(['draft', 'published'])],
'category_ids' => ['array'],
'category_ids.*' => ['exists:terms,id'],
];
}
public function messages(): array
{
return [
'title.required' => 'Please enter a title for your post.',
'content.required' => 'Post content cannot be empty.',
];
}
}
Services
Business logic encapsulated in service classes:
namespace App\Services;
class UserService
{
public function create(array $data): User
{
$user = User::create([
'first_name' => $data['first_name'],
'last_name' => $data['last_name'],
'email' => $data['email'],
'password' => Hash::make($data['password']),
]);
$user->assignRole($data['role'] ?? 'subscriber');
if ($data['send_welcome_email'] ?? false) {
$user->notify(new RegistrationWelcomeNotification());
}
return $user;
}
}
Models
Eloquent models with relationships and scopes:
namespace App\Models;
class Post extends Model
{
use HasFactory, InteractsWithMedia, HasUniqueSlug;
protected $fillable = [
'title', 'slug', 'content', 'design',
'status', 'user_id', 'parent_id',
'featured_image_id', 'published_at',
];
protected function casts(): array
{
return [
'design' => 'array',
'published_at' => 'datetime',
];
}
// Relationships
public function author(): BelongsTo
{
return $this->belongsTo(User::class, 'user_id');
}
public function categories(): BelongsToMany
{
return $this->belongsToMany(Term::class, 'term_relationships')
->where('taxonomy', 'category');
}
public function meta(): HasMany
{
return $this->hasMany(PostMeta::class);
}
// Scopes
public function scopePublished(Builder $query): Builder
{
return $query->where('status', 'published')
->where('published_at', '<=', now());
}
public function scopeSearchable(Builder $query, string $search): Builder
{
return $query->where(function ($q) use ($search) {
$q->where('title', 'like', "%{$search}%")
->orWhere('content', 'like', "%{$search}%");
});
}
}
Module System Architecture
Modules are self-contained packages with their own MVC structure:
┌─────────────────────────────────────────────────────────────────┐
│ LaraDashboard Core │
├─────────────────────────────────────────────────────────────────┤
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────────┐ │
│ │ Module A │ │ Module B │ │ Module C │ │
│ │ │ │ │ │ │ │
│ │ app/ │ │ app/ │ │ app/ │ │
│ │ config/ │ │ config/ │ │ config/ │ │
│ │ database/ │ │ database/ │ │ database/ │ │
│ │ resources/ │ │ resources/ │ │ resources/ │ │
│ │ routes/ │ │ routes/ │ │ routes/ │ │
│ └─────────────┘ └─────────────┘ └─────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
Modules communicate with core through:
- Service Providers - Register bindings and boot logic
- Hooks - Action and filter hooks for extensibility
- Events - Laravel event system
- Contracts - Interface-based communication
Hooks Architecture
The hooks system enables WordPress-like extensibility:
┌───────────────────────────────────────────────────────────────┐
│ Hook Execution Flow │
├───────────────────────────────────────────────────────────────┤
│ │
│ Module A Core Module B │
│ ┌───────┐ ┌─────────┐ ┌───────┐ │
│ │Register│───────────────▶│ Hook │◀───────────│Register│ │
│ │Listener│ │ Manager │ │Listener│ │
│ └───────┘ └────┬────┘ └───────┘ │
│ │ │
│ ┌───────────┴───────────┐ │
│ ▼ ▼ │
│ ┌──────────┐ ┌──────────┐ │
│ │ Action │ │ Filter │ │
│ │ Hooks │ │ Hooks │ │
│ └──────────┘ └──────────┘ │
│ │ │ │
│ ▼ ▼ │
│ Execute all Transform and │
│ listeners return value │
│ │
└───────────────────────────────────────────────────────────────┘
Data Flow
Content Creation Flow
User Input
│
▼
┌─────────────────┐
│ Form Request │── Validation & Authorization
└────────┬────────┘
│
▼
┌─────────────────┐
│ Controller │── Route to Service
└────────┬────────┘
│
▼
┌─────────────────┐
│ Service │── Business Logic
└────────┬────────┘
│
▼
┌─────────────────┐
│ Model │── Eloquent Operations
└────────┬────────┘
│
▼
┌─────────────────┐
│ Observer │── Side Effects
└────────┬────────┘
│
▼
┌─────────────────┐
│ Database │── Persist Data
└─────────────────┘
API Request Flow
API Request
│
▼
┌─────────────────┐
│ Sanctum Auth │── Token Validation
└────────┬────────┘
│
▼
┌─────────────────┐
│ Rate Limiting │── Request Throttling
└────────┬────────┘
│
▼
┌─────────────────┐
│ API Controller │── Process Request
└────────┬────────┘
│
▼
┌─────────────────┐
│ API Resource │── Transform Response
└────────┬────────┘
│
▼
┌─────────────────┐
│ JSON Response │
└─────────────────┘
Security Architecture
Authentication Layers
┌───────────────────────────────────────────────────────────────┐
│ Authentication Flow │
├───────────────────────────────────────────────────────────────┤
│ │
│ Web Requests API Requests │
│ ┌──────────┐ ┌──────────┐ │
│ │ Session │ │ Sanctum │ │
│ │ Guard │ │ Token │ │
│ └────┬─────┘ └────┬─────┘ │
│ │ │ │
│ └────────┬───────────┘ │
│ ▼ │
│ ┌──────────────┐ │
│ │ User │ │
│ │ Model │ │
│ └──────┬───────┘ │
│ │ │
│ ▼ │
│ ┌──────────────┐ │
│ │ Roles │ │
│ │ Permissions │ │
│ └──────────────┘ │
│ │
└───────────────────────────────────────────────────────────────┘
Permission Hierarchy
Super Admin
│
├── All Permissions (wildcard)
│
Admin
│
├── users.*
├── roles.*
├── posts.*
├── settings.*
└── modules.*
│
Editor
│
├── posts.view
├── posts.create
├── posts.edit
└── media.*
│
Author
│
├── posts.view
├── posts.create
└── posts.edit_own
Caching Strategy
LaraDashboard uses strategic caching:
| Cache Type | Driver | TTL | Use Case |
|---|---|---|---|
| Config | File | Forever | Application config |
| Routes | File | Forever | Route definitions |
| Views | File | Forever | Compiled Blade |
| Settings | Redis/File | 24h | Database settings |
| Permissions | Redis/File | 24h | RBAC cache |
| API Responses | Redis | 5-60min | Expensive queries |
Next Steps
- Directory Structure - File organization
- Module System - Build modules
- Hooks System - Extend functionality