Skip to content

Commit

Permalink
Drop unnecessary interface to call references in aggregate handlers (#…
Browse files Browse the repository at this point in the history
…349)

* remove from AroundMethodInterceptor.php

* remove from CallStateBasedAggregateService.php

* refactor call aggregate services

---------

Co-authored-by: Dariusz Gafka <dgafka.mail@gmail.com>
  • Loading branch information
jlabedo and dgafka authored Jul 26, 2024
1 parent fefc496 commit a1f9e79
Show file tree
Hide file tree
Showing 7 changed files with 119 additions and 161 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Ecotone\Messaging\Handler\Processor\MethodInvoker;

use Ecotone\Messaging\Support\InvalidArgumentException;
use function array_merge;

use Ecotone\Messaging\Config\Annotation\ModuleConfiguration\ParameterConverterAnnotationFactory;
Expand Down Expand Up @@ -31,7 +32,6 @@
use Ecotone\Messaging\Message;
use Ecotone\Messaging\MessagingException;
use Ecotone\Messaging\Precedence;
use InvalidArgumentException;

/**
* licence Apache-2.0
Expand Down Expand Up @@ -202,12 +202,16 @@ public function compile(MessagingContainerBuilder $builder, array $endpointAnnot
$converterDefinitions[] = ReferenceBuilder::create($parameter->getName(), $parameter->getTypeHint())->compile($interceptingInterface);
continue;
}
throw new InvalidArgumentException("Can't build around interceptor for {$this->interfaceToCall} because can't find converter for parameter {$parameter}");
throw InvalidArgumentException::create("Can't build around interceptor for {$this->interfaceToCall} because can't find converter for parameter {$parameter}");
}

if ($this->interfaceToCall->canReturnValue() && ! $hasMethodInvocation) {
throw InvalidArgumentException::create("Trying to register {$this->interfaceToCall} as Around Advice which can return value, but doesn't control invocation using " . MethodInvocation::class . '. Have you wanted to register Before/After Advice or forgot to type hint MethodInvocation?');
}

return new Definition(AroundMethodInterceptor::class, [
$this->directObject ?: new Reference($this->referenceName),
InterfaceToCallReference::fromInstance($this->interfaceToCall),
$this->interfaceToCall->getMethodName(),
$converterDefinitions,
$hasMethodInvocation,
]);
Expand Down
Empty file.
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,10 @@

namespace Ecotone\Messaging\Handler\Processor\MethodInvoker;

use Ecotone\Messaging\Handler\InterfaceToCall;
use Ecotone\Messaging\Handler\InterfaceToCallRegistry;
use Ecotone\Messaging\Handler\ParameterConverter;
use Ecotone\Messaging\Handler\ReferenceSearchService;
use Ecotone\Messaging\Message;
use Ecotone\Messaging\Support\InvalidArgumentException;

/**
* @author Dariusz Gafka <support@simplycodedsoftware.com>
Expand All @@ -20,11 +18,8 @@ class AroundMethodInterceptor
/**
* @param ParameterConverter[] $parameterConverters
*/
public function __construct(private object $referenceToCall, private InterfaceToCall $interceptorInterfaceToCall, private array $parameterConverters, private bool $hasMethodInvocation)
public function __construct(private object $referenceToCall, private string $methodName, private array $parameterConverters, private bool $hasMethodInvocation)
{
if ($interceptorInterfaceToCall->canReturnValue() && ! $this->hasMethodInvocation) {
throw InvalidArgumentException::create("Trying to register {$interceptorInterfaceToCall} as Around Advice which can return value, but doesn't control invocation using " . MethodInvocation::class . '. Have you wanted to register Before/After Advice or forgot to type hint MethodInvocation?');
}
}

/**
Expand Down Expand Up @@ -62,7 +57,7 @@ public function getReferenceToCall(): object

public function getMethodName(): string
{
return $this->interceptorInterfaceToCall->getMethodName();
return $this->methodName;
}

public function hasMethodInvocation(): bool
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
<?php

namespace Ecotone\Modelling\AggregateFlow\CallAggregate;

use Ecotone\Messaging\Conversion\MediaType;
use Ecotone\Messaging\Handler\ParameterConverter;
use Ecotone\Messaging\Handler\Processor\MethodInvoker\AroundMethodInterceptor;
use Ecotone\Messaging\Handler\Processor\MethodInvoker\AroundMethodInvocation;
use Ecotone\Messaging\Handler\Processor\MethodInvoker\MethodInvoker;
use Ecotone\Messaging\Handler\TypeDescriptor;
use Ecotone\Messaging\Handler\Type;
use Ecotone\Messaging\Message;
use Ecotone\Messaging\Support\Assert;
use Ecotone\Messaging\Support\MessageBuilder;
use Ecotone\Modelling\AggregateMessage;

class AggregateMethodInvoker
{

/**
* @param array<ParameterConverter> $methodParameterConverters
* @param array<string> $methodParameterNames
* @param array<AroundMethodInterceptor> $aroundMethodInterceptors
*/
public function __construct(
private string $aggregateClass,
private string $objectMethodName,
private ?Type $returnType,
private array $methodParameterNames,
private bool $isCommandHandler,
private array $methodParameterConverters,
private array $aroundMethodInterceptors,
) {
Assert::allInstanceOfType($methodParameterConverters, ParameterConverter::class);
Assert::allInstanceOfType($aroundMethodInterceptors, AroundMethodInterceptor::class);
}

public function execute(Message $message): ?MessageBuilder
{
$calledAggregate = $message->getHeaders()->containsKey(AggregateMessage::CALLED_AGGREGATE_OBJECT) ? $message->getHeaders()->get(AggregateMessage::CALLED_AGGREGATE_OBJECT) : null;

$methodInvoker = new MethodInvoker(
objectToInvokeOn: $calledAggregate ?: $this->aggregateClass,
objectMethodName: $this->objectMethodName,
methodParameterConverters: $this->methodParameterConverters,
methodParameterNames: $this->methodParameterNames,
canInterceptorReplaceArguments: false
);

if ($this->aroundMethodInterceptors) {
$methodInvokerChainProcessor = new AroundMethodInvocation($message, $this->aroundMethodInterceptors, $methodInvoker);
$result = $methodInvokerChainProcessor->proceed();
} else {
$result = $methodInvoker->executeEndpoint($message);
}

return $this->buildMessageFromResult($message, $result);
}

private function buildMessageFromResult(Message $message, mixed $result): ?MessageBuilder
{
$resultMessage = MessageBuilder::fromMessage($message);

$resultType = TypeDescriptor::createFromVariable($result);
if ($resultType->isIterable() && $this->returnType?->isCollection()) {
$resultType = $this->returnType;
}

if (! is_null($result)) {
if ($this->isCommandHandler) {
$calledAggregate = $message->getHeaders()->containsKey(AggregateMessage::CALLED_AGGREGATE_OBJECT) ? $message->getHeaders()->get(AggregateMessage::CALLED_AGGREGATE_OBJECT) : null;
$resultMessage = $resultMessage->setHeader(AggregateMessage::CALLED_AGGREGATE_OBJECT, $calledAggregate);
}

$resultMessage = $resultMessage
->setContentType(MediaType::createApplicationXPHPWithTypeParameter($resultType->toString()))
->setPayload($result)
;
}

if ($this->isCommandHandler && is_null($result)) {
$resultMessage = $resultMessage->setHeader(AggregateMessage::NULL_EXECUTION_RESULT, true);
}

if ($this->isCommandHandler || ! is_null($result)) {
return $resultMessage;
}
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
namespace Ecotone\Modelling\AggregateFlow\CallAggregate;

use Ecotone\Messaging\Config\Container\Definition;
use Ecotone\Messaging\Config\Container\InterfaceToCallReference;
use Ecotone\Messaging\Config\Container\MessagingContainerBuilder;
use Ecotone\Messaging\Config\Container\Reference;
use Ecotone\Messaging\Handler\ClassDefinition;
Expand All @@ -18,11 +17,9 @@
use Ecotone\Messaging\Handler\ServiceActivator\ServiceActivatorBuilder;
use Ecotone\Messaging\Handler\TypeDescriptor;
use Ecotone\Messaging\Support\Assert;
use Ecotone\Modelling\Attribute\AggregateEvents;
use Ecotone\Modelling\Attribute\AggregateVersion;
use Ecotone\Modelling\Attribute\EventSourcingAggregate;
use Ecotone\Modelling\Attribute\EventSourcingSaga;
use Ecotone\Modelling\EventSourcingExecutor\EventSourcingHandlerExecutorBuilder;
use Ecotone\Modelling\WithAggregateVersioning;

/**
Expand All @@ -39,11 +36,8 @@ class CallAggregateServiceBuilder extends InputOutputMessageHandlerBuilder imple
* @var bool
*/
private bool $isCommandHandler;
private Definition $eventSourcingHandlerExecutor;
private ?string $aggregateMethodWithEvents;
private ?string $aggregateVersionProperty;
private bool $isEventSourced = false;
private bool $isReturningAggregate = false;

private function __construct(ClassDefinition $aggregateClassDefinition, string $methodName, bool $isCommandHandler, InterfaceToCallRegistry $interfaceToCallRegistry)
{
Expand All @@ -56,20 +50,6 @@ private function initialize(ClassDefinition $aggregateClassDefinition, string $m
{
$interfaceToCall = $interfaceToCallRegistry->getFor($aggregateClassDefinition->getClassType()->toString(), $methodName);

$this->isReturningAggregate = $interfaceToCall->isReturningAggregate($interfaceToCallRegistry);

$aggregateMethodWithEvents = null;
$aggregateEventsAnnotation = TypeDescriptor::create(AggregateEvents::class);
foreach ($aggregateClassDefinition->getPublicMethodNames() as $method) {
$methodToCheck = $interfaceToCallRegistry->getFor($aggregateClassDefinition->getClassType()->toString(), $method);

if ($methodToCheck->hasMethodAnnotation($aggregateEventsAnnotation)) {
$aggregateMethodWithEvents = $method;
break;
}
}
$this->aggregateMethodWithEvents = $aggregateMethodWithEvents;

$eventSourcedAggregateAnnotation = TypeDescriptor::create(EventSourcingAggregate::class);
$eventSourcedSagaAnnotation = TypeDescriptor::create(EventSourcingSaga::class);
if ($interfaceToCall->hasClassAnnotation($eventSourcedAggregateAnnotation) || $interfaceToCall->hasClassAnnotation($eventSourcedSagaAnnotation)) {
Expand All @@ -91,7 +71,6 @@ private function initialize(ClassDefinition $aggregateClassDefinition, string $m
}

$this->interfaceToCall = $interfaceToCall;
$this->eventSourcingHandlerExecutor = EventSourcingHandlerExecutorBuilder::createFor($aggregateClassDefinition, $this->isEventSourced, $interfaceToCallRegistry);
$isFactoryMethod = $this->interfaceToCall->isFactoryMethod();
if (! $this->isEventSourced && $isFactoryMethod) {
Assert::isTrue($this->interfaceToCall->getReturnType()->isClassNotInterface(), "Factory method {$this->interfaceToCall} for standard aggregate should return object. Did you wanted to register Event Sourced Aggregate?");
Expand Down Expand Up @@ -165,22 +144,30 @@ public function __toString()
private function callEventSourcedAggregateServiceDefinition(array $compiledMethodParameterConverters, array $interceptors): Definition
{
return new Definition(CallEventSourcingAggregateService::class, [
InterfaceToCallReference::fromInstance($this->interfaceToCall),
$compiledMethodParameterConverters,
$interceptors,
$this->compileAggregateMethodInvoker($compiledMethodParameterConverters, $interceptors),
new Reference(PropertyReaderAccessor::class),
$this->isCommandHandler,
$this->interfaceToCall->isFactoryMethod() ?? false,
$this->aggregateVersionProperty,
]);
}

private function callStateBasedAggregateServiceDefinition(array $compiledMethodParameterConverters, array $interceptors): Definition
{
return new Definition(CallStateBasedAggregateService::class, [
InterfaceToCallReference::fromInstance($this->interfaceToCall),
$this->compileAggregateMethodInvoker($compiledMethodParameterConverters, $interceptors),
]);
}

private function compileAggregateMethodInvoker(array $compiledMethodParameterConverters, array $interceptors): Definition
{
return new Definition(AggregateMethodInvoker::class, [
$this->interfaceToCall->getInterfaceName(),
$this->interfaceToCall->getMethodName(),
$this->interfaceToCall->getReturnType(),
$this->interfaceToCall->getInterfaceParametersNames(),
$this->isCommandHandler,
$compiledMethodParameterConverters,
$interceptors,
$this->isCommandHandler,
]);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,9 @@

namespace Ecotone\Modelling\AggregateFlow\CallAggregate;

use Ecotone\Messaging\Conversion\MediaType;
use Ecotone\Messaging\Handler\Enricher\PropertyPath;
use Ecotone\Messaging\Handler\Enricher\PropertyReaderAccessor;
use Ecotone\Messaging\Handler\InterfaceToCall;
use Ecotone\Messaging\Handler\ParameterConverter;
use Ecotone\Messaging\Handler\Processor\MethodInvoker\AroundMethodInterceptor;
use Ecotone\Messaging\Handler\Processor\MethodInvoker\AroundMethodInvocation;
use Ecotone\Messaging\Handler\Processor\MethodInvoker\MethodInvoker;
use Ecotone\Messaging\Handler\TypeDescriptor;
use Ecotone\Messaging\Message;
use Ecotone\Messaging\Support\Assert;
use Ecotone\Messaging\Support\MessageBuilder;
use Ecotone\Modelling\AggregateMessage;
use Ecotone\Modelling\CallAggregateService;

Expand All @@ -24,29 +15,17 @@
*/
final class CallEventSourcingAggregateService implements CallAggregateService
{
private bool $isFactoryMethod;

/**
* @param array<ParameterConverter> $parameterConverters
* @param array<AroundMethodInterceptor> $aroundMethodInterceptors
*/
public function __construct(
private InterfaceToCall $interfaceToCall,
private array $parameterConverters,
private array $aroundMethodInterceptors,
private AggregateMethodInvoker $aggregateMethodInvoker,
private PropertyReaderAccessor $propertyReaderAccessor,
private bool $isCommandHandler,
private bool $isFactoryMethod,
private ?string $aggregateVersionProperty,
) {
Assert::allInstanceOfType($parameterConverters, ParameterConverter::class);
Assert::allInstanceOfType($aroundMethodInterceptors, AroundMethodInterceptor::class);

$this->isFactoryMethod = $this->interfaceToCall->isFactoryMethod() ?? false;
}

public function call(Message $message): ?Message
{
$resultMessage = MessageBuilder::fromMessage($message);
$resultMessage = $this->aggregateMethodInvoker->execute($message);

$calledAggregate = $message->getHeaders()->containsKey(AggregateMessage::CALLED_AGGREGATE_OBJECT) ? $message->getHeaders()->get(AggregateMessage::CALLED_AGGREGATE_OBJECT) : null;
$versionBeforeHandling = $message->getHeaders()->containsKey(AggregateMessage::TARGET_VERSION) ? $message->getHeaders()->get(AggregateMessage::TARGET_VERSION) : null;
Expand All @@ -62,41 +41,6 @@ public function call(Message $message): ?Message
$resultMessage = $resultMessage->setHeader(AggregateMessage::TARGET_VERSION, $versionBeforeHandling);
}

$methodInvoker = new MethodInvoker(
objectToInvokeOn: $calledAggregate ?: $this->interfaceToCall->getInterfaceType()?->toString(),
objectMethodName: $this->interfaceToCall->getMethodName(),
methodParameterConverters: $this->parameterConverters,
methodParameterNames: $this->interfaceToCall->getInterfaceParametersNames(),
canInterceptorReplaceArguments: false
);

if ($this->aroundMethodInterceptors) {
$methodInvokerChainProcessor = new AroundMethodInvocation(requestMessage: $message, aroundMethodInterceptors: $this->aroundMethodInterceptors, interceptedMessageProcessor: $methodInvoker);
$result = $methodInvokerChainProcessor->proceed();
} else {
$result = $methodInvoker->executeEndpoint($message);
}

$resultType = TypeDescriptor::createFromVariable($result);
if ($resultType->isIterable() && $this->interfaceToCall->getReturnType()?->isCollection()) {
$resultType = $this->interfaceToCall->getReturnType();
}

if (! is_null($result)) {
if ($this->isCommandHandler) {
$resultMessage = $resultMessage->setHeader(AggregateMessage::CALLED_AGGREGATE_OBJECT, $calledAggregate);
}

$resultMessage = $resultMessage
->setContentType(MediaType::createApplicationXPHPWithTypeParameter($resultType->toString()))
->setPayload($result)
;
}

if ($this->isCommandHandler || ! is_null($result)) {
return $resultMessage->build();
}

return null;
return $resultMessage->build();
}
}
Loading

0 comments on commit a1f9e79

Please sign in to comment.