schorts / shared-kernel
Shared kernel value objects and domain abstractions
v0.10.7
2026-08-04 01:31 UTC
README
Shared kernel value objects and domain abstractions for building domain-driven PHP applications.
📦 Installation
Require the package via Composer:
composer require schorts/shared-kernel
Composer will autoload classes under the namespace:
Schorts\SharedKernel\
For example, classes in src/ValueObjects/ are available under:
use Schorts\SharedKernel\ValueObjects\EmailValue;
🧩 Features
-
Value Objects
Immutable primitives with validation and equality logic:
ArrayValueBooleanValueCoordinatesValueDateValueEmailValueEnumValueFloatValueIntegerValueObjectValuePhoneValueSlugValueStringValueURLValueUUIDValue
-
Domain Events
DomainEventbase class with metadata and primitives serialization.DomainEventMetadataandDomainEventPrimitivesDTOs.
-
Entities
Entitybase class with identity and domain event recording.
-
Aggregate Roots
AggregateRootabstract class with versioning, uncommitted-change tracking, domain-event recording / pulling (with sequence numbers), snapshots andfromPrimitives/fromSnapshotfactory methods.
-
DAO Abstraction
DAOcontract for persistence operations with support forUnitOfWorkand delete modes.
-
CQRS – Queries
Queryinterface andAbstractQuerybase class with metadata, correlation/causation IDs, headers and context.QueryMetadataandQueryPrimitivesDTOs.- Built-in error types:
QueryNotRegisteredQueryAlreadyRegistered
-
CQRS – Query Handlers
QueryHandlerinterface (validate → authorize → execute pipeline).AbstractQueryHandlerwith optional caching, logging and metrics support.QueryHandlerOptionsandQueryHandlerContextDTOs.- Pluggable
CacheandLoggercontracts. - Built-in error types:
QueryAuthorizationErrorQueryExecutionErrorQueryValidationError
-
CQRS – Query Bus
QueryBusinterface for registering handlers and dispatching queries.InMemoryQueryBusimplementation with middleware support.QueryBusMiddleware,QueryBusContextandQueryBusConfig.- Bulk dispatch (
dispatchMany) withAggregateErroron partial failures.
-
i18n
TranslationResolvercontract for localised messages.
🚀 Usage
Example: Creating a Value Object
use Schorts\SharedKernel\ValueObjects\EmailValue; class UserEmail extends EmailValue { public function getAttributeName(): string { return 'user_email'; } } $email = new UserEmail('test@example.com'); if ($email->isValid()) { echo "Valid email: " . $email; }
Example: Defining a Domain Event
use Schorts\SharedKernel\DomainEvent\DomainEvent; class UserRegisteredEvent extends DomainEvent { public function getEventName(): string { return 'user.registered'; } }
Example: Entity with Domain Events
use Schorts\SharedKernel\Entity\Entity; use Schorts\SharedKernel\ValueObjects\ValueObject; use Schorts\SharedKernel\Model\Model; class UserEntity extends Entity { public function toPrimitives(): Model { // return DTO representation } }
Example: Aggregate Root
use Schorts\SharedKernel\AggregateRoot\AggregateRoot; use Schorts\SharedKernel\ValueObjects\UUIDValue; use Schorts\SharedKernel\DomainEvent\DomainEvent; final class UserId extends UUIDValue { public function getAttributeName(): string { return 'user_id'; } } final class UserRegistered extends DomainEvent { public function getEventName(): string { return 'user.registered'; } } /** * @extends AggregateRoot<UserId> */ final class User extends AggregateRoot { private string $email; private string $name; public function __construct(UserId $id, string $email, string $name, int $version = 0) { parent::__construct($id, $version); $this->email = $email; $this->name = $name; } public static function register(UserId $id, string $email, string $name): self { $user = new self($id, $email, $name); $user->recordDomainEvent(new UserRegistered(/* … */)); return $user; } public function toPrimitives(): array { return [ 'email' => $this->email, 'name' => $this->name, ]; } protected function restoreFromPrimitives(array $data): void { $this->email = $data['email']; $this->name = $data['name']; } } // Create & record events $user = User::register(new UserId('…'), 'john@example.com', 'John'); // Pull events (with sequence numbers) and clear the internal buffer $events = $user->pullDomainEvents(); // Snapshot / restore $snapshot = $user->toSnapshot(); $restored = User::fromSnapshot($snapshot);
Example: Defining a Query
use Schorts\SharedKernel\Query\AbstractQuery; final class GetUserByIdQuery extends AbstractQuery { public function __construct( public readonly string $userId, string $correlationId, ?array $customMetadata = null, ) { parent::__construct($correlationId, $customMetadata); } public function getType(): string { return 'user.get_by_id'; } public function toPrimitives(): \Schorts\SharedKernel\Query\QueryPrimitives { $primitives = parent::toPrimitives(); // You can enrich payload here if needed return $primitives; } }
Example: Implementing a Query Handler
use Schorts\SharedKernel\Query\AbstractQueryHandler; use Schorts\SharedKernel\Query\QueryHandlerContext; /** * @extends AbstractQueryHandler<GetUserByIdQuery, array> */ final class GetUserByIdHandler extends AbstractQueryHandler { public function execute(Query $query, QueryHandlerContext $context): mixed { /** @var GetUserByIdQuery $query */ // Fetch user from repository / DAO … return [ 'id' => $query->userId, 'name' => 'John Doe', ]; } // Optional overrides: // public function validate(Query $query): void { … } // public function authorize(Query $query): void { … } // public function getCacheKey(Query $query): ?string { … } }
Example: Using the Query Bus
use Schorts\SharedKernel\Query\InMemoryQueryBus; use Schorts\SharedKernel\Query\QueryBusConfig; $bus = new InMemoryQueryBus(); $bus->register('user.get_by_id', new GetUserByIdHandler( new \Schorts\SharedKernel\Query\QueryHandlerOptions( cache: true, cacheTtl: 300_000, // 5 minutes logging: true, ) )); // Optional middleware $bus->use(new class implements \Schorts\SharedKernel\Query\QueryBusMiddleware { public function beforeDispatch(Query $query, \Schorts\SharedKernel\Query\QueryBusContext $context): void { // e.g. start timer, add tracing span } public function afterDispatch(Query $query, mixed $result, \Schorts\SharedKernel\Query\QueryBusContext $context): void { // e.g. record metrics } public function onError(Query $query, \Throwable $error, \Schorts\SharedKernel\Query\QueryBusContext $context): void { // e.g. log / alert } }); $query = new GetUserByIdQuery( userId: 'user-123', correlationId: 'corr-abc-123', ); $result = $bus->dispatch($query);
Example: Bulk Dispatch
$results = $bus->dispatchMany([ new GetUserByIdQuery('user-1', 'corr-1'), new GetUserByIdQuery('user-2', 'corr-2'), ]);
📜 License
LGPL-3.0-or-later