Extension points
Contract has two kinds of interfaces. Some, like ContainerInterface or ConnectionInterface, describe classes you consume: you type hint against them and let a Raxos package provide the implementation. Others are meant to be implemented by you, so you can plug custom behavior into a Raxos package. This page is about the second kind.
Small interfaces you implement
Extension point interfaces are intentionally small, usually one or two methods, so implementing one is a quick way to hook into a package without subclassing its internals. Common examples:
CasterInterface: custom ORM column types in raxos/database.MiddlewareInterface: pipeline steps in raxos/router.HandlerInterface: message consumers in raxos/message-bus.PolicyInterface: query shaping in raxos/search.MutationListenerInterface: reacting to model property changes in raxos/database.ConstraintAttributeInterface: custom validation constraints in raxos/http.TransformerInterface: reshaping a validated request value in raxos/http.
Why the interface lives here
Because the interface lives in Contract rather than in the package that consumes it, your implementation does not need to depend on that package's concrete classes at all. You depend on the small contract, the package depends on the same contract, and the two meet without being coupled.
The consuming package usually needs to know which of your classes to call. That link is made with an attribute you attach to your implementation or to the property that uses it, for example the #[Caster] attribute on a model column.
Example: a custom ORM caster
CasterInterface is a typical extension point. raxos/database calls into it, your application supplies the implementation, and the #[Caster] attribute wires the two together.
<?php
declare(strict_types=1);
namespace App\Casters;
use Raxos\Contract\Database\Orm\CasterInterface;
use Raxos\Database\Orm\Model;
final readonly class MoneyCaster implements CasterInterface
{
public function decode(string|float|int|null $value, Model $instance): mixed
{
return $value === null ? null : (int) $value;
}
public function encode(mixed $value, Model $instance): string|float|int|null
{
return $value === null ? null : (string) $value;
}
}See the CasterInterface reference for the full method contract, and the extension point references for the router and message bus equivalents.