jord-jd/uxdm
UXDM is a PHP data migration library for moving records between sources and destinations such as CSV, JSON, XML, PDO and Eloquent. Map fields, transform values, and run repeatable imports or exports through a simple, extensible API.
Architecture fit
UXDM is a framework-agnostic PHP data-migration library, not a Laravel package. Its model is a linear pipeline: one or more sources (array, PDO/database, CSV-style file sources) feed migrators (item-level field mapping, field transformers, relation handling) and emit to one or more destinations (array, database, file). That is a reasonable fit for the class of work Laravel teams routinely hand-roll: seeding a new schema from a legacy CRM/ERP export, backfilling a normalized table from a denormalized one, or re-platforming between two apps.
Importantly, it is a build-time/tooling dependency, not a runtime dependency. Nothing in the product request path touches UXDM; it runs as a batch job, an Artisan command, or a queued worker. That materially lowers the blast radius of adopting a low-adoption package, and it means the architecture decision is really "do we adopt this as our ETL scaffold, or write ~300–600 lines of bespoke chunked-mapping code?"
Where it fits poorly: anything event-driven, CDC/incremental-sync, or requiring strong transactional guarantees across heterogeneous systems. UXDM is oriented at batch, run-to-completion migrations. If our roadmap needs ongoing two-way sync, this is the wrong tool and we should say so early.
Integration feasibility
Composer-installable, no service provider, no facade, no published config — so integration is deliberately manual. In a Laravel codebase that means we own the wiring: an artisan command per migration, a config/migrations/*.php style registry, logging via our existing channels, and wrapping each run in a job so long migrations do not block deploys. That is a day or two of scaffolding, not a week.
The fluent, chainable API is the main draw: field mapping reads close to declarative config, which is reviewable by non-authors and diffable in PRs. That is a genuine advantage over a bespoke foreach loop for auditability during a cutover.
The friction points are Laravel-native ergonomics we do not get for free: no queue/event hooks, no built-in progress reporting, no chunk/resume semantics we can rely on without verifying, and no first-class Eloquent support (it works at the PDO layer).
Technical risk
Key questions
Stack fit
Good fit as a migration workbench inside the Laravel monolith, invoked from Artisan commands and queued jobs. It complements rather than competes with Laravel's schema migrations: Laravel owns DDL, UXDM owns DML for cross-system loads. We should avoid introducing it into request-path code or into packages we publish.
Migration path
Adopt behind an anti-corruption layer. Define our own thin interfaces — SourceReader, RecordMapper, DestinationWriter — and implement them with UXDM internally. Cost: roughly 2–3 days. Benefit: if the package stalls, the swap to a bespoke chunked writer is a contained change rather than a rewrite, and unit tests can target our interfaces with fixtures instead of the vendor's objects.
Compatibility
Framework-agnosticism is a double-edged property: we are insulated from Laravel major-version churn, but we also inherit zero Laravel integration. Concrete compatibility checks before commitment: PHP constraint vs. our runtime, PDO driver coverage (MySQL/Postgres/SQLite), behavior under declare(strict_types=1) and our static-analysis baseline (PHPStan/Larastan level), and whether it plays nicely with our test suite's database transactions.
Sequencing
Maintenance
Treat this as a vendored dependency with an internal owner, not a community package. Pin the exact version in composer.json, commit composer.lock, and mirror the source in our VCS so an upstream deletion cannot break a rebuild. Budget roughly 1–2 engineer-days per quarter for upgrade triage; more if we fork.
Support
There is no vendor and no SLA. Escalation is "the named DRI on our team reads the source." That DRI must exist before adoption — if we cannot name them, we should not take the dependency. Community support at 164 stars means issue turnaround should be assumed to be weeks or never.
Scaling
Assume batch throughput is bounded by destination write performance, not by the library. Levers we control: disable non-clustered indexes and FK checks during load and rebuild after, batch sizes tuned to memory, parallelize by disjoint entity sets, and run against a read replica for the source. If per-record object overhead proves to be the ceiling above ~500k rows, the fallback is a bespoke PDO::FETCH_ASSOC streaming loop — which is precisely why the adapter layer matters.
Failure modes
Ranked by likelihood × impact:
INSERT ... ON DUPLICATE KEY UPDATE semantics.Every one of these is a job failure, not a user-facing outage — which is the core argument for the risk being acceptable.
Ramp-up
The library itself is a half-day to learn; the fluent API is small and self-documenting. Realistic ramp-up for a mid-level Laravel engineer to productive-and-safe is 3–5 days, of which the majority is spent on domain mapping, reconciliation strategy, and the runbook — not on UXDM. That is the right way to frame the cost/benefit: we are buying roughly 1–2 weeks of ETL scaffolding, and paying for it with a supply-chain risk that must be capped by the adapter layer and the fork-ready posture.
How can I help you explore Laravel packages today?