Skip to content

Instantly share code, notes, and snippets.

@webdevilopers
Last active July 15, 2020 08:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save webdevilopers/687c8b34d68e97a8f93df5db09242406 to your computer and use it in GitHub Desktop.
Save webdevilopers/687c8b34d68e97a8f93df5db09242406 to your computer and use it in GitHub Desktop.
Creating event-sourced aggregate roots through CQRS read models
<?php
namespace AcmersonnelManagement\Domain\Model\EmploymentContract;
final class EmploymentContract extends AggregateRoot
{
/** @var EmploymentContractId $employmentContractId */
private $id;
/** @var PersonId $personId */
private $personId;
/** @var EmploymentPeriod */
private $employmentPeriod;
public static function sign(
EmploymentContractId $anId, PersonId $aPersonId, PersonalData $aPersonalData, ContractType $aContractType,
DateTimeImmutable $aStartDate, ?DateTimeImmutable $anEndDate, ?DateTimeImmutable $aProbationEndDate,
WorkerCategory $aWorkerCategory, WageType $aWageType, bool $aWorkingTimeAccount,
WorkweekDays $aWorkWeekDays, WeeklyWorkingHours $aWeeklyWorkingHours,
HolidayEntitlement $aHolidayEntitlement, AdditionalLeave $additionalLeave,
JobFunctionId $aJobFunctionId, string $aJobFunctionName,
EmployerId $anEmployerId, string $anEmployerName, WorkplaceId $aWorkplaceId, string $aWorkplaceName
): EmploymentContract
{
$employmentPeriod = EmploymentPeriod::withType($aContractType, $aStartDate, $anEndDate);
$self = new self();
$self->recordThat(EmploymentContractSigned::withData(
$anId, $aPersonId, $aPersonalData, $aContractType, $employmentPeriod, $aPbationaryPeriod,
$aWorkerCategory, $aWageType, $aWorkingTimeAccount,
$aWorkWeekDays, $aWeeklyWorkingHours,
$aHolidayEntitlement, $additionalLeave,
$aJobFunctionId, $aJobFunctionName,
$anEmployerId, $anEmployerName, $aWorkplaceId, $aWorkplaceName,
new DateTimeImmutable()
));
return $self;
}
protected function apply(AggregateChanged $event): void
{
switch (get_class($event)) {
/** @var EmploymentContractSigned $event */
case EmploymentContractSigned::class:
$this->id = $event->contractId();
$this->personId = $event->personId();
$this->employmentPeriod = $event->employmentPeriod();
break;
}
}
public function aggregateId(): string
{
return $this->id->toString();
}
}
<?php
namespace Acme\PersonnelManagement\Domain\Model\Person;
final class PersonReadModel
{
private $personId;
private $personalData;
private $employmentContracts;
public function signEmploymentContract(
EmploymentContractId $contractId, ContractType $contractType,
DateTimeImmutable $startDate, ?DateTimeImmutable $endDate, ?DateTimeImmutable $aProbationEndDate,
...
): EmploymentContract {
$employmentPeriod = EmploymentPeriod::withType($contractType, $startDate, $endDate);
if (!OverlappingEmploymentContractPolicy::isSatisfiedBy(
$contractId, $contractType, $employmentPeriod, ..., $this->employmentContracts
)) {
throw new EmploymentPeriodOverlapsException();
}
return EmploymentContract::sign(
$contractId, $this->personId, $this->personalData,
$contractType, $startDate(), $endDate(), $probationEndDate(),
...
);
}
}
<?php
namespace Acme\PersonnelManagement\Application\Service\EmploymentContract;
final class SignEmploymentContractHandler
{
/** @var EmploymentContractEventStoreRepository */
private $contractCollection;
/** @var PersonDetailsRepository */
private $personDetailsRepository;
/** @var ContractDetailsRepository */
private $contractsDetailsRepository;
public function __construct(
EmploymentContractEventStoreRepository $contractCollection,
PersonDetailsRepository $personDetailsRepository,
ContractDetailsRepository $contractsDetailsRepository
)
{
$this->contractCollection = $contractCollection;
$this->personDetailsRepository = $personDetailsRepository;
$this->contractsDetailsRepository = $contractsDetailsRepository;
}
public function __invoke(SignEmploymentContract $command): void
{
$person = $this->personDetailsRepository->ofPersonId($command->personId()->toString());
$enteredContracts = $this->contractsDetailsRepository->ofPersonId($person->personId());
if (!OverlappingEmploymentContractPolicy::isSatisfiedBy(
$command->contractId(), $command->contractType(),
$command->employmentPeriod(), $command->employerId(), $enteredContracts
)) {
throw new EmploymentPeriodOverlapsException();
}
$contract = EmploymentContract::sign(
$command->contractId(), $command->personId(),
new PersonalData(...),
$command->contractType(),
$command->startDate(), $command->endDate(), $command->probationEndDate(),
$command->workerCategory(), $command->wageType(),
$command->workingTimeAccount(), $command->workweekDays(),
$command->weeklyWorkingHours(), $command->holidayEntitlement(), $command->additionalLeave(),
$command->jobFunctionId(), $jobFunction->name(),
$command->employerId(), $employer->name(),
$command->workplaceId(), $workplace->name()
);
$this->contractCollection->save($contract);
}
}
@webdevilopers
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment