02 — Architecture
Back to README | Prev: Project Overview | Next: Database Design
Tech Stack
Core Technologies
| Layer | Technology | Purpose |
|---|---|---|
| Backend | Laravel 12 | Application framework, API, code generation |
| Frontend Components | Livewire 4 | Server-driven UI for pages, forms, CRUD |
| Client Interactivity | Alpine.js | Canvas interactions, drag-and-drop, real-time UI |
| Canvas Rendering | SVG (via Alpine.js) | Table nodes, relationship lines, visual elements |
| Styling | Tailwind CSS 4 | UI design and responsive layout |
| Testing | Pest PHP | Unit, feature, and integration testing |
SaaS-Specific Technologies
| Layer | Technology | Purpose |
|---|---|---|
| Authentication | Laravel Breeze | User registration, login, profile |
| Real-time | Laravel Reverb | WebSocket-based team collaboration |
| Payments | Laravel Cashier (Stripe) | Subscription billing |
| Queue | Laravel Queues | Background jobs (export, generation) |
Package-First Monorepo
The core functionality lives as a standalone Laravel package inside packages/schemacraft/. The host Laravel app simply installs the package and adds SaaS features on top.
SchemaCraft/
│
├── packages/
│ └── schemacraft/ <- Open-source Laravel package
│ ├── src/
│ │ ├── SchemaCraftServiceProvider.php
│ │ ├── Http/
│ │ │ ├── Controllers/ <- Export controller
│ │ │ └── Livewire/ <- Canvas, table editor, project manager
│ │ ├── Models/ <- Project, Table, Column, Relationship, Index
│ │ ├── Enums/ <- ColumnType, RelationshipType
│ │ ├── Generators/ <- Migration, Model, Factory, Seeder generators
│ │ └── Services/ <- SchemaExportService, LaravelProjectGenerator
│ ├── resources/
│ │ └── views/ <- Blade + inline Alpine.js views
│ ├── config/ <- schemacraft.php config
│ ├── database/migrations/ <- 5 schema tables
│ ├── routes/ <- web.php with package routes
│ └── composer.json
│
├── app/ <- Host Laravel application
│ ├── app/
│ │ ├── Models/User.php
│ │ └── Http/ <- Auth, billing, teams (SaaS only)
│ ├── composer.json <- Requires "schemacraft/schemacraft:@dev"
│ └── ...
│
└── docs/ <- This documentation
Why Package-First?
- Reusability: The package works in any Laravel project via
composer require - Separation of concerns: Core schema design logic is decoupled from SaaS features
- Community contributions: Open-source contributors improve both package and SaaS
- Independent testing: Package has its own test suite
- Clean upgrades: SaaS app pulls package updates without merge conflicts
How the Package Integrates
The root composer.json uses a path repository to symlink the package during development:
{
"repositories": [
{
"type": "path",
"url": "packages/schemacraft"
}
],
"require": {
"schemacraft/schemacraft": "@dev"
}
}
This means vendor/schemacraft/schemacraft is a symlink to packages/schemacraft/, so changes in the package are immediately reflected without re-running composer update.
Feature Distribution
| Feature | Open-source Package | SaaS App |
|---|---|---|
| Visual canvas editor | Yes | Yes (via package) |
| Table/column/relationship design | Yes | Yes (via package) |
| Generate migrations | Yes | Yes (via package) |
| Generate models, factories, seeders | Yes | Yes (via package) |
| Export as ZIP | Yes | Yes (via package) |
| Import from existing database | Yes | Yes (via package) |
| User authentication | No | Yes |
| Cloud save projects | No | Yes |
| Team collaboration (real-time) | No | Yes |
| Version history | No | Yes |
| Share via public link | No | Yes |
| Billing / subscriptions | No | Yes |
| Custom code templates | No | Yes (premium) |
| AI-assisted schema generation | No | Yes (premium) |
Canvas Architecture
The visual canvas is the core of SchemaCraft. It uses a hybrid Alpine.js + Livewire approach to achieve smooth 60fps interactions with persistent server state.
Data Flow
User drags a table on canvas
│
Alpine.js updates position instantly (zero lag, pure client-side)
│
On mouse release → Alpine dispatches event to Livewire
│
Livewire persists new position to database
│
Other users get update via Laravel Reverb (collaboration — future)
Why This Works
| Concern | Handled By | Why |
|---|---|---|
| Visual rendering | Alpine.js + SVG | Instant updates, no server roundtrips |
| User interactions | Alpine.js | Mouse events, keyboard shortcuts, 60fps |
| Data persistence | Livewire | Clean server-side saves on meaningful events |
| DOM management | wire:ignore |
Prevents Livewire from re-rendering the SVG |
| State sync | Events ($wire.$on) |
Livewire pushes updates to Alpine without full re-render |
Canvas Technical Details
- SVG container with
<pattern>elements for grid background (minor + major lines) - Transform group
<g :transform="viewTransform">applies pan and zoom to all content - Table nodes are
<g>groups containing rects, text, and column rows - Relationship lines are
<line>or<path>elements with<marker>for cardinality (crow's foot, vertical line) - Coordinate conversion uses
getScreenCTM()— the standard SVG approach that automatically accounts for all transforms (pan, zoom, viewport offsets)
Why Not React/Vue?
- Stays in the Laravel ecosystem — no separate JS build, no API layer needed
- SVG elements are DOM nodes — Alpine.js binds directly to them with
x-on,x-bind,x-data - Simpler deployment — no Node.js runtime dependency in production
- Livewire handles the hard parts — form validation, persistence, server logic
- Alpine.js is already included — ships with Livewire, zero additional dependencies
Key Technical Decisions
| Decision | Rationale |
|---|---|
wire:ignore on SVG canvas |
Prevents Livewire DOM-diffing on complex SVG; Alpine handles visuals, Livewire handles persistence |
schemacraft_ table prefix |
Avoids database collisions when installed in existing Laravel apps |
| No JS build step in package | Package ships Blade + inline Alpine.js; host app's Vite handles compilation |
getScreenCTM() for coordinates |
Standard SVG method; accounts for all transforms automatically |
| MigrationGenerator as pure PHP service | Testable in isolation, reusable from future CLI commands, no HTTP dependency |
| Events for Livewire-Alpine sync | $wire.$on('table-added', ...) pushes data into Alpine without full re-render |
| JSON columns for flexible data | canvas_settings, line_points, enum_values, column_ids — schema can evolve without migrations |
Code Generation Architecture
SchemaCraft uses a generator pattern to produce Laravel code from visual schemas. All generators follow the same contract and can be composed together or used independently.
Generator Pattern
interface GeneratorContract
{
/**
* Generate all files for a project.
*
* @return array<string, string> Filename => content
*/
public function generateForProject(Project $project): array;
}
Available Generators (Phase 2)
| Generator | Output | Location |
|---|---|---|
| MigrationGenerator | Laravel migration files | database/migrations/ |
| ModelGenerator | Eloquent models with relationships | app/Models/ |
| FactoryGenerator | Factory files with Faker methods | database/factories/ |
| SeederGenerator | Seeder files with dependency ordering | database/seeders/ |
Code Generation Flow
Project (with Tables, Columns, Relationships)
↓
┌───────────────┬───────────────┬───────────────┬───────────────┐
│ Migration │ Model │ Factory │ Seeder │
│ Generator │ Generator │ Generator │ Generator │
└───────┬───────┴───────┬───────┴───────┬───────┴───────┬───────┘
│ │ │ │
└───────────────┴───────────────┴───────────────┘
↓
CodePreview Component
(Category tabs: Migrations, Models, Factories, Seeders)
↓
SchemaExportService
(ZIP with proper Laravel directory structure)
Generator Features
MigrationGenerator (Phase 1):
- All 30+ Laravel column types with proper parameters
- Column modifiers (nullable, default, unique, index)
- Foreign key constraints with cascade options
- Pivot table migrations for belongsToMany relationships
ModelGenerator (Phase 2):
$fillablearray (excludes id, timestamps, deleted_at)casts()method with intelligent type mapping$hiddenarray for sensitive fields (password, remember_token)- Typed relationship methods (hasOne, hasMany, belongsTo, belongsToMany)
- SoftDeletes trait when applicable
- Timestamps configuration
FactoryGenerator (Phase 2):
- Intelligent Faker method selection based on column type
- Special handling for common columns (email, password, slug)
- Foreign key handling via
Model::factory()references - State methods (unverified, trashed, status states)
- Nullable column support with
fake()->optional()
SeederGenerator (Phase 2):
- Individual seeder per table
- Master DatabaseSeeder with dependency ordering (Kahn's algorithm)
- Pivot table attachment for belongsToMany relationships
- Factory-based data creation
Why This Pattern?
| Benefit | Description |
|---|---|
| Testable | Each generator is a pure PHP class with no HTTP/Livewire dependency |
| Composable | Generators can be used together or separately |
| Reusable | Can be called from controllers, commands, jobs, queues |
| Extensible | New generators follow the same pattern and can be added without changing existing code |
| Consistent | All return ['filename' => 'content'] arrays |
See Phase 2 Implementation for detailed generator architecture and examples.
LaravelProjectGenerator (Phase 2.5)
LaravelProjectGenerator composes all four generators to produce a complete, ready-to-run Laravel + Filament project rather than individual files:
Project
↓
LaravelProjectGenerator
├── Copies base Laravel 12 template
├── Injects generated migrations, models, factories, seeders
├── Adds AdminPanelProvider + Filament config
├── Creates pre-generated .env with APP_KEY
├── Injects SetupController + routes + welcome wizard
└── Creates ZIP archive
↓
User downloads and runs:
composer install && php artisan serve
→ Opens browser → clicks "Run Setup" → done
See Phase 2.5 Implementation for full details.
Service Provider Overview
The SchemaCraftServiceProvider is the package entry point. It:
- Merges config from
config/schemacraft.php - Loads routes from
routes/web.php(with configurable prefix and middleware) - Loads views namespaced as
schemacraft::(e.g.,schemacraft::layouts.app) - Publishes migrations to the host app
- Registers Livewire components via
Livewire::addNamespace('schemacraft', ...)
Configuration
The package exposes these settings via config/schemacraft.php:
return [
'route_prefix' => 'schemacraft', // URL prefix for all routes
'middleware' => ['web'], // Middleware applied to routes
'default_canvas' => [
'zoom' => 1,
'pan_x' => 0,
'pan_y' => 0,
'grid_size' => 20,
'snap_to_grid' => true,
],
'default_table' => [
'width' => 250,
'color' => '#3B82F6',
],
];
Next: Database Design