gdbots/acme-schemas
gdbots/acme-schemas supplies PHP schema definitions for the Acme domain, giving Laravel applications typed classes for messages and events so data stays consistent across services. Install with Composer to share a common contract for serialization and validation.
Know what you're installing first. gdbots/acme-schemas is explicitly described as test schemas for common gdbots services — 0 stars, 0 dependents, last release mid-2023. Treat it as a reference implementation and a fixture package, not a domain library. Your realistic first use case is one of two things:
gdbots/pbjx / gdbots/schemas and need a known-good schema package to exercise message construction, validation, and serialization before you write your own.yourvendor/yourdomain-schemas and want a working layout to copy.Install alongside the runtime it depends on:
composer require gdbots/acme-schemas gdbots/schemas
Note the pinned constraint in v3.0.2: it requires gdbots/schemas v3.0.3. If you're already on a newer schemas major, expect a conflict — resolve that before you write any code.
Where to look first, in order:
composer.json — the autoload map tells you where the generated message classes live (typically src/Acme/... with a Gdbots\Schemas\Acme\... namespace) and how the schemas themselves are shipped.schemas/acme/.../1.0.0.json or similar). These are the actual contract — read them before you read a single generated class.First working snippet. Construct a message, validate it, and round-trip it:
use Gdbots\Schemas\Acme\...\Article;
$article = Article::create()
->set('title', 'Hello')
->set('body', 'World');
$article->validate(); // throws on missing/invalid fields
$binary = $article->toBinary(); // PBJ, for storage / queues
$array = $article->toArray(); // plain array, for JSON APIs
$again = Article::fromBinary($binary); // round-trip check
If that round-trip works, your resolver and autoloading are wired correctly and you can move on to real integration work.
Register the schema resolver once, at boot. Schema resolution is global in the gdbots stack, so the correct place is a service provider's register() — not a controller, not a job constructor.
public function register(): void
{
$this->app->singleton(SchemaResolver::class, function () {
$resolver = new SchemaResolver();
// register Acme schemas so curies like acme:acme:article:1.0.0 resolve
return $resolver;
});
}
Map HTTP input to messages at the boundary. Keep controllers thin: a FormRequest validates shape, then a small mapper builds the message. The message's own validate() is the authoritative check — don't duplicate required-field logic in Laravel validation rules, or the two will drift.
$article = Article::create()
->set('title', $request->validated('title'))
->set('body', $request->validated('body'));
Serialize by destination, not by habit. PBJ binary for storage and for message-bus payloads; toArray()/JSON only at the API edge. Mixing the two is the single most common source of "it works in tests, breaks in the queue" bugs.
Carry messages through jobs as opaque payloads. A queued job should hold the binary (or base64 thereof) and rehydrate with fromBinary(). Never serialize the message object itself into the job — the class carries schema state that won't survive PHP's serializer cleanly across deploys.
Pin curies explicitly when crossing a boundary. Anything persisted or published should record the full curie (acme:acme:article:1.0.0), not just a class name. That's what lets you add 1.1.0 later and still read old records.
Treat generated code as read-only and versioned. Generated classes are an output, not a place to patch. The day-to-day workflow is: edit the schema file → regenerate → commit both → bump the schema curie version.
Use it as a test fixture layer. Since these are test schemas, the highest-value pattern is building integration tests for your own PBJ-consuming services against them — you get realistic curies, enums, and required-field behaviour without polluting your assertions with production domain data.
Evolve schemas additively. New optional fields with defaults in a new minor curie version; never repurpose or remove a field in a released schema. Keep the old schema available so historic records still hydrate.
The package name is the warning label. "Test schemas for common gdbots services" means the field names and message types are illustrative. If you build a real domain on top of it, you inherit a contract nobody is maintaining. Copy the structure into your own *-schemas package; don't depend on this one in production.
Version pinning is brittle. v3.0.2 targets gdbots/schemas v3.0.3 specifically. Laravel projects on a newer gdbots runtime will hit a Composer conflict, and with zero dependents there's no community pressure to release a compatible tag. Check composer why-not gdbots/schemas early, and be prepared to fork.
Missing required fields fail at build/validate time, not at the database. A QueryException on a NOT NULL column means your message validation never ran. Call validate() immediately after construction and let it throw in the boundary layer.
Curie mismatches throw at resolution, not at import. The class exists (autoload doesn't care), but the resolver can't find the schema. When you see a resolution failure, dump the curie string and compare character-by-character against the schema filename — a wrong vendor segment or a 1.0.0 vs 1.0 is nearly always the cause.
Enums are typed, not strings. Setting a raw string where the schema declares an enum will fail validation even though it "looks right". Use the generated enum class constants; this is the second-most-common first-day error after curie mismatch.
Debugging tips. Work backwards from the schema JSON rather than the generated PHP — the JSON is the source of truth and the generated class is a mechanical translation. For serialization oddities, toArray() is far easier to eyeball than toBinary(); dump that first, then confirm the binary round-trips.
Generated code and composer dump-autoload. After regenerating schemas, cached classmaps will keep serving stale classes. A plain composer dump-autoload (and php artisan optimize:clear if you've cached config/classes) resolves 90% of "the field I added doesn't exist" reports.
Extension points are in the base library, not here. Custom validation, schema building, and resolvers all come from gdbots/schemas / gdbots/pbj. Subclass the builder or attach validation there — you cannot meaningfully extend anything inside acme-schemas itself.
Release cadence tells you the support story. One release in 2023, no dependents, no issues traffic. Budget for reading the source, not for waiting on upstream fixes; if this package is load-bearing for you, vendor it or fork it explicitly.
How can I help you explore Laravel packages today?