Skip to content

Commit 77f384d

Browse files
committed
refactor after merge
1 parent a616384 commit 77f384d

File tree

9 files changed

+111
-75
lines changed

9 files changed

+111
-75
lines changed

packages/Ecotone/src/AnnotationFinder/AnnotatedDefinition.php

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,23 @@
99

1010
/**
1111
* licence Apache-2.0
12+
*
13+
* @template TClassAttribute of object
14+
* @template TMethodAttribute of object
15+
* @implements AnnotatedFinding<TMethodAttribute>
1216
*/
1317
class AnnotatedDefinition implements AnnotatedFinding
1418
{
1519
private string $className;
1620
private string $methodName;
21+
/**
22+
* @var TClassAttribute
23+
*/
1724
private object $annotationForClass;
25+
26+
/**
27+
* @var TMethodAttribute
28+
*/
1829
private object $annotationForMethod;
1930
/**
2031
* @var object[]
@@ -25,6 +36,13 @@ class AnnotatedDefinition implements AnnotatedFinding
2536
*/
2637
private array $classAnnotations;
2738

39+
/**
40+
* @param TClassAttribute $annotationForClass
41+
* @param TMethodAttribute $annotationForMethod
42+
* @param class-string $className
43+
* @param object[] $classAnnotations
44+
* @param object[] $methodAnnotations
45+
*/
2846
private function __construct(object $annotationForClass, object $annotationForMethod, string $className, string $methodName, array $classAnnotations, array $methodAnnotations)
2947
{
3048
$this->annotationForClass = $annotationForClass;
@@ -36,6 +54,9 @@ private function __construct(object $annotationForClass, object $annotationForMe
3654
}
3755

3856
/**
57+
* @param TClassAttribute $annotationForClass
58+
* @param TMethodAttribute $annotationForMethod
59+
* @param class-string $className
3960
* @param object[] $classAnnotations
4061
* @param object[] $methodAnnotations
4162
*/
@@ -44,16 +65,25 @@ public static function create(object $annotationForClass, object $annotationForM
4465
return new self($annotationForClass, $annotationForMethod, $className, $methodName, $classAnnotations, $methodAnnotations);
4566
}
4667

68+
/**
69+
* @return TClassAttribute
70+
*/
4771
public function getAnnotationForClass(): object
4872
{
4973
return $this->annotationForClass;
5074
}
5175

76+
/**
77+
* @return TMethodAttribute
78+
*/
5279
public function getAnnotationForMethod(): object
5380
{
5481
return $this->annotationForMethod;
5582
}
5683

84+
/**
85+
* @return class-string
86+
*/
5787
public function getClassName(): string
5888
{
5989
return $this->className;

packages/Ecotone/src/AnnotationFinder/AnnotatedFinding.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,19 @@
66

77
/**
88
* licence Apache-2.0
9+
*
10+
* @template TMethodAttribute of object
911
*/
1012
interface AnnotatedFinding
1113
{
14+
/**
15+
* @return TMethodAttribute
16+
*/
1217
public function getAnnotationForMethod(): object;
1318

19+
/**
20+
* @return class-string
21+
*/
1422
public function getClassName(): string;
1523

1624
public function getMethodName(): string;

packages/Ecotone/src/AnnotationFinder/AnnotationFinder.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,11 @@
1010
interface AnnotationFinder extends AnnotationResolver
1111
{
1212
/**
13-
* @return AnnotatedDefinition[]
13+
* @template TClassAttribute of object
14+
* @template TMethodAttribute of object
15+
* @param class-string<TClassAttribute> $classAnnotationName
16+
* @param class-string<TMethodAttribute> $methodAnnotationClassName
17+
* @return list<AnnotatedDefinition<TClassAttribute,TMethodAttribute>>
1418
*/
1519
public function findCombined(string $classAnnotationName, string $methodAnnotationClassName): array;
1620

packages/Ecotone/src/AnnotationFinder/AnnotationResolver.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,12 @@ interface AnnotationResolver
1313
public function getAnnotationsForMethod(string $className, string $methodName): array;
1414

1515
/**
16+
* @template T of object
1617
* @param class-string $className
17-
* @param class-string|null $attributeClassName
18-
* @return list<object>
18+
* @param class-string<T>|null $attributeClassName
19+
* @return ($attributeClassName is null ? list<object> : list<T>)
1920
*/
20-
public function getAnnotationsForClass(string $className, ?string $attributeClassName): array;
21+
public function getAnnotationsForClass(string $className, ?string $attributeClassName = null): array;
2122

2223
/**
2324
* @return object[]

packages/Ecotone/src/AnnotationFinder/AnnotationResolver/AttributeResolver.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ public function getAnnotationsForMethod(string $className, string $methodName):
4646
/**
4747
* @inheritDoc
4848
*/
49-
public function getAnnotationsForClass(string $className): array
49+
public function getAnnotationsForClass(string $className, ?string $attributeClassName = null): array
5050
{
5151
$attributes = [];
5252
$currentClass = new ReflectionClass($className);
@@ -58,6 +58,9 @@ public function getAnnotationsForClass(string $className): array
5858
if (! class_exists($attribute->getName())) {
5959
continue;
6060
}
61+
if ($attributeClassName && ! ($attribute instanceof $attributeClassName)) {
62+
continue;
63+
}
6164
if (in_array($attribute->getName(), array_map(fn ($attr) => $attr::class, $attributes))) {
6265
continue; // Avoid duplicate attributes from parent classes
6366
}

packages/Ecotone/src/AnnotationFinder/FileSystem/FileSystemAnnotationFinder.php

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -245,11 +245,18 @@ private function getAnnotationForClass(string $className, string $annotationClas
245245
}
246246

247247
/**
248+
* @param string $className
249+
* @param string|null $attributeClassName
248250
* @inheritDoc
249251
*/
250-
public function getAnnotationsForClass(string $className): array
252+
public function getAnnotationsForClass(string $className, ?string $attributeClassName = null): array
251253
{
252-
return $this->getCachedAnnotationsForClass($className);
254+
$attributes = $this->getCachedAnnotationsForClass($className);
255+
if ($attributeClassName) {
256+
return array_values(array_filter($attributes, fn (object $attribute) => $attribute instanceof $attributeClassName));
257+
} else {
258+
return $attributes;
259+
}
253260
}
254261

255262
/**

packages/Ecotone/src/AnnotationFinder/InMemory/InMemoryAnnotationFinder.php

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -105,13 +105,17 @@ public function getAnnotationsForMethod(string $className, string $methodName):
105105
/**
106106
* @inheritDoc
107107
*/
108-
public function getAnnotationsForClass(string $classNameToFind): array
108+
public function getAnnotationsForClass(string $className, ?string $attributeClassName = null): array
109109
{
110-
if (! isset($this->annotationsForClass[self::CLASS_ANNOTATIONS][$classNameToFind])) {
110+
if (! isset($this->annotationsForClass[self::CLASS_ANNOTATIONS][$className])) {
111111
return [];
112112
}
113+
$attributes = $this->annotationsForClass[self::CLASS_ANNOTATIONS][$className];
114+
if ($attributeClassName) {
115+
$attributes = array_filter($attributes, fn ($attribute) => $attribute instanceof $attributeClassName);
116+
}
113117

114-
return array_values($this->annotationsForClass[self::CLASS_ANNOTATIONS][$classNameToFind]);
118+
return array_values($attributes);
115119
}
116120

117121
/**

packages/PdoEventSourcing/src/Config/ProophProjectingModule.php

Lines changed: 44 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
use Ecotone\EventSourcing\Projecting\PartitionState\DbalProjectionStateStorageBuilder;
2121
use Ecotone\EventSourcing\Projecting\StreamSource\EventStoreAggregateStreamSourceBuilder;
2222
use Ecotone\EventSourcing\Projecting\StreamSource\EventStoreGlobalStreamSourceBuilder;
23+
use Ecotone\EventSourcing\Projecting\StreamSource\EventStoreMultiStreamSourceBuilder;
2324
use Ecotone\Messaging\Attribute\ModuleAnnotation;
2425
use Ecotone\Messaging\Config\Annotation\AnnotationModule;
2526
use Ecotone\Messaging\Config\Annotation\ModuleConfiguration\ExtensionObjectResolver;
@@ -40,9 +41,6 @@
4041
use Ecotone\Projecting\EventStoreAdapter\EventStreamingChannelAdapter;
4142

4243
#[ModuleAnnotation]
43-
/**
44-
* @phpstan-type ProjectionConfiguration array{projectionName: string, streamName: string, aggregateType: ?string, isPartitioned: bool, eventNames: array}
45-
*/
4644
class ProophProjectingModule implements AnnotationModule
4745
{
4846
/**
@@ -57,8 +55,6 @@ public function __construct(
5755

5856
public static function create(AnnotationFinder $annotationRegistrationService, InterfaceToCallRegistry $interfaceToCallRegistry): static
5957
{
60-
$extensions = [];
61-
6258
$namedEvents = [];
6359
foreach ($annotationRegistrationService->findAnnotatedClasses(NamedEvent::class) as $className) {
6460
$attribute = $annotationRegistrationService->getAttributeForClass($className, NamedEvent::class);
@@ -67,14 +63,7 @@ public static function create(AnnotationFinder $annotationRegistrationService, I
6763

6864
$projectionEventNames = self::collectProjectionEventNames($annotationRegistrationService, $interfaceToCallRegistry, $namedEvents);
6965

70-
$resolvedConfigs = [
71-
...self::resolveFromStreamConfigs($annotationRegistrationService, $projectionEventNames),
72-
...self::resolveFromAggregateStream($annotationRegistrationService, $projectionEventNames),
73-
];
74-
75-
foreach ($resolvedConfigs as $config) {
76-
$extensions = [...$extensions, ...self::createStreamSourceExtensions($config)];
77-
}
66+
$extensions = self::resolveConfigs($annotationRegistrationService, $projectionEventNames);
7867

7968
$projectionNames = [];
8069
foreach ($annotationRegistrationService->findAnnotatedClasses(ProjectionV2::class) as $projectionClassName) {
@@ -91,20 +80,23 @@ public static function create(AnnotationFinder $annotationRegistrationService, I
9180
/**
9281
* Resolve stream configurations from FromStream attributes.
9382
*
94-
* @return list<ProjectionConfiguration>
83+
* @return list<ProjectionComponentBuilder>
9584
*/
96-
private static function resolveFromStreamConfigs(
85+
private static function resolveConfigs(
9786
AnnotationFinder $annotationRegistrationService,
98-
array $projectionEventNames
99-
): array {
100-
$configs = [];
87+
array $projectionEventNames
88+
): array
89+
{
90+
$extensions = [];
10191

10292
foreach ($annotationRegistrationService->findAnnotatedClasses(ProjectionV2::class) as $classname) {
10393
$projectionAttribute = $annotationRegistrationService->getAttributeForClass($classname, ProjectionV2::class);
104-
$aggregateStreamAttributes = $annotationRegistrationService->getAnnotationsForClass($classname, FromAggregateStream::class);
10594
$streamAttributes = [
10695
...$annotationRegistrationService->getAnnotationsForClass($classname, FromStream::class),
107-
...array_map(fn (FromAggregateStream $attribute) => self::resolveFromAggregateStream($annotationRegistrationService, $attribute, $projectionAttribute->name), $aggregateStreamAttributes)
96+
...\array_map(
97+
fn(FromAggregateStream $aggregateStreamAttribute) => self::resolveFromAggregateStream($annotationRegistrationService, $aggregateStreamAttribute, $projectionAttribute->name),
98+
$annotationRegistrationService->getAnnotationsForClass($classname, FromAggregateStream::class)
99+
)
108100
];
109101
$partitionedAttribute = $annotationRegistrationService->findAttributeForClass($classname, Partitioned::class);
110102

@@ -115,20 +107,41 @@ private static function resolveFromStreamConfigs(
115107
$projectionName = $projectionAttribute->name;
116108
$isPartitioned = $partitionedAttribute !== null;
117109

118-
if ($isPartitioned && ! $streamAttributes->aggregateType) {
119-
throw ConfigurationException::create("Aggregate type must be provided for projection {$projectionName} as partition header name is provided");
110+
$sources = [];
111+
foreach ($streamAttributes as $streamAttribute) {
112+
if ($isPartitioned && ! $streamAttribute->aggregateType) {
113+
throw ConfigurationException::create("Aggregate type must be provided for projection {$projectionName} as partition header name is provided");
114+
}
115+
if ($isPartitioned) {
116+
$sources[$streamAttribute->stream.'.'.$streamAttribute->aggregateType] = new EventStoreAggregateStreamSourceBuilder(
117+
$projectionName,
118+
$streamAttribute->aggregateType,
119+
$streamAttribute->stream,
120+
$projectionEventNames[$projectionName] ?? [],
121+
);
122+
$extensions[] = new AggregateIdPartitionProviderBuilder(
123+
$projectionName,
124+
$streamAttribute->aggregateType,
125+
$streamAttribute->stream,
126+
);
127+
} else {
128+
$sources[$streamAttribute->stream] = new EventStoreGlobalStreamSourceBuilder(
129+
$streamAttribute->stream,
130+
[$projectionName]
131+
);
132+
}
133+
}
134+
if (count($sources) > 1) {
135+
$extensions[] = new EventStoreMultiStreamSourceBuilder(
136+
$sources,
137+
[$projectionName],
138+
);
139+
} else {
140+
$extensions[] = current($sources);
120141
}
121-
122-
$configs[] = [
123-
'projectionName' => $projectionName,
124-
'streamName' => $streamAttributes->stream,
125-
'aggregateType' => $streamAttributes->aggregateType,
126-
'isPartitioned' => $isPartitioned,
127-
'eventNames' => $projectionEventNames[$projectionName] ?? [],
128-
];
129142
}
130143

131-
return $configs;
144+
return $extensions;
132145
}
133146

134147
/**
@@ -155,38 +168,6 @@ private static function resolveFromAggregateStream(
155168
return new FromStream($streamName, $aggregateType, $aggregateStreamAttribute->eventStoreReferenceName);
156169
}
157170

158-
/**
159-
* Create stream source extensions based on resolved configuration.
160-
*
161-
* @param ProjectionConfiguration $config
162-
* @return ProjectionComponentBuilder[]
163-
*/
164-
private static function createStreamSourceExtensions(array $config): array
165-
{
166-
if ($config['isPartitioned']) {
167-
return [
168-
new EventStoreAggregateStreamSourceBuilder(
169-
$config['projectionName'],
170-
$config['aggregateType'],
171-
$config['streamName'],
172-
$config['eventNames'],
173-
),
174-
new AggregateIdPartitionProviderBuilder(
175-
$config['projectionName'],
176-
$config['aggregateType'],
177-
$config['streamName']
178-
),
179-
];
180-
}
181-
182-
return [
183-
new EventStoreGlobalStreamSourceBuilder(
184-
$config['streamName'],
185-
[$config['projectionName']],
186-
),
187-
];
188-
}
189-
190171
public function prepare(Configuration $messagingConfiguration, array $extensionObjects, ModuleReferenceSearchService $moduleReferenceSearchService, InterfaceToCallRegistry $interfaceToCallRegistry): void
191172
{
192173
$dbalConfiguration = ExtensionObjectResolver::resolveUnique(DbalConfiguration::class, $extensionObjects, DbalConfiguration::createWithDefaults());

packages/PdoEventSourcing/tests/Projecting/Global/MultiStreamProjectionTest.php

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,6 @@ public function test_reset_and_delete_on_multi_stream_projection(): void
9393

9494
private function createMultiStreamProjection(): object
9595
{
96-
$connection = $this->getConnection();
97-
9896
// Configure FromStream with multiple streams: Calendar/Meeting aggregates
9997
// Real-world usage: projection reacts to Calendar/Meeting events to generate a read model
10098
return new #[ProjectionV2(self::NAME), FromStream(CalendarWithInternalRecorder::class), FromStream(MeetingWithEventSourcing::class)] class () {

0 commit comments

Comments
 (0)