Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Merge event-sourced aggregate roots (A+ES) by passing read model / decision model
<?php
final class EmploymentContract extends AggregateRoot
{
/**
* @param Details $contractToMerge
* This contract is always the initial contract and the oldest one.
* Since the contract to merge is always newer it always overwrites the current state.
*/
public function mergeWith(Details $contractToMerge): void
{
$mergeWithContractId = EmploymentContractId::fromString($contractToMerge->contractId());
if ($this->isMergedWith($mergeWithContractId)) {
throw new AlreadyMergedWithEmploymentContractException();
}
// Always use the latest employment end date.
$newEmploymentPeriod = EmploymentPeriodMerger::merge([
$this->employmentPeriod,
new EmploymentPeriod($contractToMerge->startDate(), $contractToMerge->endDate())
]);
$newProbationaryPeriod = null;
if (null !== $contractToMerge->probationEndDate()) {
// Since we always use the latest employment end date any probation end date will fit.
$newProbationaryPeriod = ProbationaryPeriod::withEmploymentPeriod(
$newEmploymentPeriod, $contractToMerge->probationEndDate()
);
}
$this->recordThat(MergedWithEmploymentContract::with(
$this->id,
$mergeWithContractId,
ContractType::fromString($contractToMerge->contractType()),
$newEmploymentPeriod,
$newProbationaryPeriod,
WorkerCategory::fromString($contractToMerge->workerCategory()),
WageType::fromString($contractToMerge->wageType()),
$contractToMerge->workingTimeAccount(),
WorkweekDays::fromArray($contractToMerge->workweekDays()),
WeeklyWorkingHours::fromFloat($contractToMerge->weeklyWorkingHours()),
HolidayEntitlement::fromInteger($contractToMerge->holidayEntitlement()),
AdditionalLeave::fromInteger($contractToMerge->additionalLeave()),
JobFunctionId::fromInteger($contractToMerge->jobFunctionId()),
$contractToMerge->jobFunctionName(),
EmployerId::fromString($contractToMerge->employerId()),
$contractToMerge->employerName(),
WorkplaceId::fromString($contractToMerge->workplaceId()),
$contractToMerge->workplaceName(),
new DateTimeImmutable()
));
}
private function isMergedWith(EmploymentContractId $contractId): bool
{
foreach ($this->mergedWithContractIds as $mergedWithContractId)
{
if ($mergedWithContractId->sameValueAs($contractId)) {
return true;
}
}
return false;
}
}
<?php
final class EmploymentContractMergeService
{
/** @var DetailsRepository */
private $detailsRepository;
/** @var EmploymentContractRepository */
private $contractRepository;
public function __construct(
DetailsRepository $detailsRepository,
EmploymentContractRepository $contractRepository
)
{
$this->detailsRepository = $detailsRepository;
$this->contractRepository = $contractRepository;
}
/**
* @param PersonId $personId
* @param EmploymentContractId[] $mergeWithContractIds
*/
public function with(PersonId $personId, array $mergeWithContractIds): void
{
$mergeWithContracts = [];
foreach ($mergeWithContractIds as $mergeWithContractId) {
$mergeWithContractDetails = $this->detailsRepository->ofId($mergeWithContractId->toString());
$mergeWithContracts[] = $mergeWithContractDetails;
}
// Ensure chronological order of start of employment in order to detect the initial contract.
uasort($mergeWithContracts, function(Details $a, Details $b) {
return $a->startDate() <=> $b->startDate();
});
// The initial contract will be used for merging.
$initialContractId = $mergeWithContracts[0]->contractId();
$initialContract = $this->contractRepository->get(EmploymentContractId::fromString($initialContractId));
unset($mergeWithContracts[0]);
// Start the actual merging.
foreach ($mergeWithContracts as $mergeWithContract) {
// Pass read (decision) model to aggregate root which holds the actual merging logic / decisions.
$initialContract->mergeWith($mergeWithContract);
}
$this->contractRepository->save($initialContract);
}
}
@webdevilopers

This comment has been minimized.

Copy link
Owner Author

@webdevilopers webdevilopers commented Aug 30, 2020

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