Architecture

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?

  1. Reusability: The package works in any Laravel project via composer require
  2. Separation of concerns: Core schema design logic is decoupled from SaaS features
  3. Community contributions: Open-source contributors improve both package and SaaS
  4. Independent testing: Package has its own test suite
  5. 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):

  • $fillable array (excludes id, timestamps, deleted_at)
  • casts() method with intelligent type mapping
  • $hidden array 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:

  1. Merges config from config/schemacraft.php
  2. Loads routes from routes/web.php (with configurable prefix and middleware)
  3. Loads views namespaced as schemacraft:: (e.g., schemacraft::layouts.app)
  4. Publishes migrations to the host app
  5. 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

Loading...