Shared kernel value objects and domain abstractions for building domain-driven PHP applications.
Require the package via Composer:
composer require schorts/shared-kernelComposer will autoload classes under the namespace:
Schorts\SharedKernel\For example, classes in src/ValueObjects/ are available under:
use Schorts\SharedKernel\ValueObjects\EmailValue;-
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.
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;
}use Schorts\SharedKernel\DomainEvent\DomainEvent;
class UserRegisteredEvent extends DomainEvent {
public function getEventName(): string {
return 'user.registered';
}
}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
}
}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);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;
}
}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 { β¦ }
}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);$results = $bus->dispatchMany([
new GetUserByIdQuery('user-1', 'corr-1'),
new GetUserByIdQuery('user-2', 'corr-2'),
]);LGPL-3.0-or-later