orchestra/contracts
Orchestra Contracts provides the shared interfaces and abstractions that underpin the Orchestra Platform for Laravel. It keeps packages loosely coupled and consistent, making Orchestra-based applications easier to build, extend, and maintain.
orchestra/contracts is an interfaces-only package — no service provider, no config file, no routes, no migrations. "Getting started" means confirming you belong in the Orchestra graph and then reading PHP interfaces, not docs.
composer why-not orchestra/contracts against your target Laravel version. This single command is the fastest go/no-go. If you're not already on orchestra/foundation or orchestra/testbench, stop — Illuminate\Contracts\* plus PSR is the lower-risk default.composer require orchestra/contracts:^7.0
src/ first — it is the documentation. The repo contains interface definitions for extension discovery, application access, shared memory, authorization bridging, notifier, and widget abstractions. There is no narrative doc worth reading before the interfaces themselves.orchestra/foundation and siblings. The contracts are not self-documenting; their semantics live in the concrete classes that satisfy them.First real use case: you're shipping a package into the Orchestra ecosystem and want to type-hint Orchestra\Contracts\... instead of depending on the full platform, keeping your classes swappable and testable. Second: you're building an adapter layer and want a pre-built, Laravel-native vocabulary instead of inventing your own ports.
Type-hint the contract, resolve the implementation. Standard Laravel container pattern — the value is that consumers never name a concrete Orchestra class:
use Orchestra\Contracts\Extension\Factory as ExtensionFactory;
class MyPackageServiceProvider extends ServiceProvider
{
public function register(): void
{
$this->app->singleton(MyAggregator::class, function ($app) {
return new MyAggregator($app->make(ExtensionFactory::class));
});
}
}
Adapter-first, dependency-second. The workflow that keeps the seam honest:
A future re-platform of the admin layer then touches adapters, not feature modules.
Implement the Orchestra interface directly when your package is the extension point (custom extension driver, widget, notifier). Keep the implementing class thin and delegate to domain services:
use Orchestra\Contracts\Extension\Factory;
class MyExtensionFactory implements Factory
{
public function __construct(private ExtensionRegistry $registry) {}
// delegate every method to $this->registry
}
Fake it in tests. Because these are interfaces, bind a fake in the container for unit tests and skip the whole platform:
$this->app->instance(
\Orchestra\Contracts\Extension\Factory::class,
new class implements \Orchestra\Contracts\Extension\Factory { /* ... */ }
);
Or, if you're on Orchestra Testbench, let it provide real bindings and assert against behaviour instead.
Guard against signature drift with a conformance test. Add a test that simply asserts your adapter implements the interface and that signatures line up — a minor upstream interface change then fails in CI with a clear message instead of a fatal type error at boot:
public function test_adapter_implements_orchestra_contract(): void
{
$this->assertInstanceOf(\Orchestra\Contracts\Extension\Factory::class, new MyExtensionFactory(...));
}
Centralize the pin. If more than one service consumes the contracts, publish a shared internal package or a Composer platform constraint so one upstream major bump doesn't fan out into N coordinated upgrades.
Fork path for teams that want only the vocabulary. The interfaces are small, MIT-licensed, and stable in shape. Forking them into App\Contracts is a legitimate pattern — it removes upgrade-gating risk at the cost of maintaining a few hundred lines in-house. Decide this before you need it.
illuminate type-hints negate portability. The interfaces reference Illuminate\Contracts\Foundation\Application, Illuminate\Support\Collection, and so on. If your goal was framework-agnostic ports, this package doesn't deliver it — a hand-written narrow interface set is a cleaner boundary.composer why-not is the whole go/no-go. Run it against your target Laravel version before planning any work. Make it a CI pre-merge check to catch illuminate range collisions early.How can I help you explore Laravel packages today?