laravel-domain-core maintained by alex-kassel
Laravel Domain Core
A high-cohesion platform foundation package for Laravel 11.x, 12.x, and 13.x applications. alex-kassel/laravel-domain-core consolidates Domain Context Registration, Dynamic Multi-Database Migrations, Context-Aware Base Eloquent Models, Standardized Operator CLI Execution, Distributed Lock Management, Graceful Overlap Recovery, and Child Package Scaffolding.
Subsystems & Architecture
- Domain Context Registration (
DomainRegistryInterface): Runtime registration, discovery, resolution, enablement management, and caching of domain contexts, database connection names, and table prefixes. - Central
domainsTable Schema & Slug Immutability: Owns the platform-wide centraldomainsdatabase table (id,class,slug,created_at) with automatic slug resolution and mismatch/collision enforcement (DomainSlugCollisionException,DomainSlugMismatchException). - Dynamic Multi-Database Migrations (
MigrationManagerInterface): Independent migration manager supporting SQLite, MySQL, and PostgreSQL with composite migration identity tracking ({$packageSlug}:{$domainSlug}:{$filename}) and 2-stage SQLite database auto-provisioning. - Context-Aware Base Eloquent Models (
ContextAwareModel&HasDomainContextTrait): Base Eloquent models and traits that dynamically route database connections and table prefixes at runtime based on the activeDomainContext. - Standardized Operator CLI DX (
CommandRunnerInterface): Uniform Artisan command options (--all,--domains,--except-domains,--force,--dry-run) across platform packages. - Distributed Lock Management (
ExecutionLockManagerInterface): Cache/Redis locks per domain and component to prevent overlapping execution across hosts. - Graceful Overlap Recovery (
SKIPPED): Automatically handles active execution locks by returningCommandExecutionReport(status: 'SKIPPED'), printing operator warnings, and emittingCommandRunSkippedDueToOverlapdiagnostic events. - Child Package Generator (
domain-core:make-domain): Scaffolds publication-ready child domain package skeletons with complete directory structure, configuration, providers, and test setups.
Installation
Install the package via Composer:
composer require alex-kassel/laravel-domain-core
The Service Provider AlexKassel\DomainCore\Providers\DomainCoreServiceProvider is automatically registered via Laravel Package Discovery.
Developer Quick Start Guide
1. Registering Domain Contexts (DomainRegistryInterface)
Domain contexts define database connection names, table prefixes, and domain identifiers:
use AlexKassel\DomainCore\Contracts\DomainRegistryInterface;
use AlexKassel\DomainCore\DTOs\DomainContext;
$registry = app(DomainRegistryInterface::class);
$registry->register(new DomainContext(
domainSlug: 'domain-one',
packageSlug: 'alex-kassel/package-one',
connectionName: 'sqlite_domain_one',
tablePrefix: 'd1_',
className: App\Domains\DomainOneContext::class,
autoCreateSqliteDatabase: true
));
// Resolve context by slug
$context = $registry->resolve('domain-one');
echo $context->connectionName; // 'sqlite_domain_one'
echo $context->tablePrefix; // 'd1_'
2. Context-Aware Base Eloquent Models
Extend ContextAwareModel or use HasDomainContextTrait to dynamically route Eloquent queries to the correct database connection and prefix at runtime based on $domainSlug:
use AlexKassel\DomainCore\Database\Models\ContextAwareModel;
class DomainItem extends ContextAwareModel
{
/**
* Bind this model dynamically to the registered domain context.
*/
protected ?string $domainSlug = 'domain-one';
protected $fillable = ['title', 'sku', 'price'];
}
// Queries automatically use connection 'sqlite_domain_one' and table 'd1_domain_items'
$items = DomainItem::where('price', '>', 100)->get();
3. Running Deterministic Multi-Database Migrations
use AlexKassel\DomainCore\Contracts\MigrationManagerInterface;
$migrationManager = app(MigrationManagerInterface::class);
// Run migrations for a registered domain slug directly
$report = $migrationManager->migrateDomain('domain-one');
echo "Executed " . count($report->executedMigrations) . " migration(s) in " . $report->durationSeconds . "s\n";
Each migration is recorded in the migrations table using composite key {$packageSlug}:{$domainSlug}:{$filename} to prevent filename collisions across packages sharing database connections.
4. CLI Execution & Overlap Protection (CommandRunnerInterface)
Use CommandRunnerInterface in your custom Artisan console commands to parse standard flags and safely execute domain operations under lock protection:
use AlexKassel\DomainCore\Contracts\CommandRunnerInterface;
use AlexKassel\DomainCore\DTOs\CommandOptionsDTO;
use AlexKassel\DomainCore\DTOs\DomainContext;
$runner = app(CommandRunnerInterface::class);
// 1. Parse raw options from Artisan input
$options = $runner->parseCliOptions([
'all' => true,
'domains' => 'domain-one,domain-two',
'except-domains' => 'domain-two',
'force' => false,
'dry-run' => false,
]);
// 2. Resolve target domain contexts matching flags
$targetDomains = $runner->resolveTargetDomains($options);
// 3. Execute domain callback with automatic lock acquisition and SKIPPED recovery
foreach ($targetDomains as $domain) {
$report = $runner->executeDomain(
domain: $domain,
componentKey: 'runner',
callback: function (DomainContext $context, CommandOptionsDTO $opts) {
// Process domain logic
return 42; // Returns executed items count
},
options: $options
);
if ($report->status === 'SKIPPED') {
// Automatically logged; event CommandRunSkippedDueToOverlap dispatched
}
}
5. Artisan Console Commands Catalog
All package commands are grouped under the domain-core: namespace:
# Display registration, connection, prefix, and status across domains
php artisan domain-core:status
# Execute pending database migrations across all or specified registered domains
php artisan domain-core:migrate
php artisan domain-core:migrate --domain=domain-one
# Compile and cache registered domain contexts for production
php artisan domain-core:cache
# Clear compiled domain context cache
php artisan domain-core:clear
# Scaffold a new child domain package directory
php artisan domain-core:make-domain domain-one --vendor=alex-kassel
6. Scaffolding Child Packages (domain-core:make-domain)
Scaffold a standardized, publication-ready child domain package under packages/{vendor}/{slug}:
php artisan domain-core:make-domain domain-one --vendor=alex-kassel
Scaffolded directory layout:
packages/alex-kassel/domain-one/
├── composer.json
├── config/domain.php
├── src/
│ ├── Console/
│ ├── DTOs/
│ ├── Models/
│ └── Providers/
│ └── DomainOneServiceProvider.php
└── tests/
└── Unit/
Exception Taxonomy & Handling
All package exceptions implement AlexKassel\DomainCore\Exceptions\DomainCoreExceptionInterface:
DomainCoreExceptionInterface (Marker Interface)
├── DomainNotFoundException (thrown when resolving an unregistered/disabled domain slug)
├── DomainConnectionNotFoundException (thrown when database file/connection resolution fails)
├── DomainSlugCollisionException (thrown when a requested slug collides with another domain class)
├── DomainSlugMismatchException (thrown when a registered domain class changes its slug)
├── MigrationFailedException (thrown when migration execution or rollback fails)
├── DomainResolutionException (thrown when target CLI domain filtering fails)
├── LockAcquisitionException (thrown when lock backend connection fails critically)
└── ScaffoldingException (thrown when package scaffolding fails due to directory collision)
Testing
Run the PHPUnit test suite:
composer test
License
The MIT License (MIT). Please see License File for more information.