You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The best way to get started with `Ecotone` is to actually build something realistic.\
9
+
The best way to get started with **Ecotone** is to actually build something realistic.\
10
10
Therefore we will build a small back-end for Shopping System during this tutorial. \
11
11
The techniques we will learn in the tutorial are **fundamental to building any application using Ecotone**. 
12
12
13
13
{% hint style="success" %}
14
-
To contribute to documentation, create Pull Request in [Documentation repository](https://github.com/ecotoneframework/documentation).
14
+
Found something to improve in the docs?\
15
+
Create Pull Request in [Documentation repository](https://github.com/ecotoneframework/documentation).
15
16
{% endhint %}
16
17
17
18
## Lessons
18
19
19
20
The tutorial is divided into several lessons:
20
21
21
-
*[Lesson 1](php-messaging-architecture.md), we will learn **the fundamentals** of _Ecotone_: Endpoints, Messages, Channels, Gateways and using Command Query Responsibility Segregation (CQRS) on top of that
22
-
*[Lesson 2](php-domain-driven-design.md), we will learn using **Tactical**[**Domain Driven Design (DDD)**](../modelling/modelling-1.md): Aggregates, Repositories and also Event Handlers
23
-
*[Lesson 3](php-serialization-deserialization.md), we will learn **how to use Converters**
24
-
*[Lesson 4](php-metadata-method-invocation.md), we will learn about **Metadata and Method Invocation**
25
-
*[Lesson 5](php-interceptors-middlewares.md), we will learn about **Interceptors**, to handle cross cutting concerns
26
-
*[Lesson 6](php-asynchronous-processing.md), we we will learn about **Asynchronous** Endpoints
22
+
*[Lesson 1](php-messaging-architecture.md), we will learn **the fundamentals** of **Ecotone**: Endpoints, Messages, Channels, and Command Query Responsibility Segregation (**CQRS**)
23
+
*[Lesson 2](php-domain-driven-design.md), we will learn **TacticalDomain Driven Design (DDD)**: Aggregates, Repositories and also Event Handlers
24
+
*[Lesson 3](php-serialization-deserialization.md), we will learn **how to use Converters,** therefore how to handle serialization and deserialization
25
+
*[Lesson 4](php-metadata-method-invocation.md), we will learn about **Metadata and Method Invocation** - How we can execute Message Handlers in a way not available in any other PHP Framework
26
+
*[Lesson 5](php-interceptors-middlewares.md), we will learn about **Interceptors**, Ecotone's powerful Middlewares
27
+
*[Lesson 6](php-asynchronous-processing.md), we we will learn about **Asynchronous** Endpoints, so how to process our Messages asynchronously.
27
28
28
29
{% hint style="success" %}
29
30
You don’t have to complete all of the lessons at once to get the value out of this tutorial. \
Copy file name to clipboardExpand all lines: tutorial-php-ddd-cqrs-event-sourcing/php-metadata-method-invocation.md
+66-40
Original file line number
Diff line number
Diff line change
@@ -12,14 +12,18 @@ Not having code for _Lesson 4?_ \
12
12
13
13
### Metadata
14
14
15
-
Message can contain headers. Headers may contain anything e.g. `currentUser`, `timestamp`, `contentType`, `messageId.` Headers are helpful, to give extra insights about the message and how it should be handled. \
16
-
In `Ecotone` headers and metadata means the same. Those terms will be used interchangeably.
15
+
Message can contain of Metadata. Metadata is just additional information stored along side to the Message's payload. It may contain things like **currentUser**, **timestamp**, **contentType**, **messageId**. 
17
16
18
-
We just got new requirement for our Products in Shopping System.:
17
+
{% hint style="info" %}
18
+
In **Ecotone** headers and metadata means the same. Those terms will be used interchangeably.
19
+
{% endhint %}
20
+
21
+
\
22
+
To test out Metadata, let's assume we just got new requirement for our Products in Shopping System.:
19
23
20
-
`User who registered the product, should be able to change it price.` 
24
+
> User who registered the product, should be able to change it's price. 
21
25
22
-
Let's start by adding `ChangePriceCommand.`
26
+
Let's start by adding **ChangePriceCommand**
23
27
24
28
```php
25
29
namespace App\Domain\Product;
@@ -42,8 +46,8 @@ class ChangePriceCommand
42
46
}
43
47
```
44
48
45
-
We will handle this `Command` in a minute. Let's first add user information for registering the product.\
46
-
We will do it, using `Meta Data`. Let's get back to our Testing Class `EcotoneQuickstart` and add 4th argument to our `CommandBus` call.
49
+
We will handle this Command in a minute. Let's first add user information for registering the product.\
50
+
We will do it, using **Metadata**. Let's get back to our Testing Class **EcotoneQuickstart** and add 4th argument to our **CommandBus** call.
47
51
48
52
```php
49
53
public function run() : void
@@ -52,7 +56,7 @@ public function run() : void
52
56
"product.register",
53
57
\json_encode(["productId" => 1, "cost" => 100]),
54
58
"application/json",
55
-
[
59
+
metadata: [
56
60
"userId" => 1
57
61
]
58
62
);
@@ -61,17 +65,17 @@ public function run() : void
61
65
}
62
66
```
63
67
64
-
We call `sendWithRouting`, it accepts 4th argument, which is `associative array.` Whatever we will place in here, will be available during message handling for us.\
65
-
So it's good place, to enrich the message with extra information, which we will want to use or store, but are is not part of the `command/query/event`. \
66
-
Now we can change our `Product` aggregate: 
68
+
**sendWithRouting**accepts 4th argument, which is **associative array.** Whatever we will place in here, will be available during message handling for us - This actually our Metadata. It's super simple to pass new Headers, it's matter of adding another key to the array.\
69
+
\
70
+
Now we can change our **Product** aggregate: 
67
71
68
72
```php
69
73
#[Aggregate]
70
74
class Product
71
75
{
72
76
use WithAggregateEvents;
73
77
74
-
#[AggregateIdentifier]
78
+
#[Identifier]
75
79
private int $productId;
76
80
77
81
private Cost $cost;
@@ -90,13 +94,19 @@ class Product
90
94
#[CommandHandler("product.register")]
91
95
public static function register(RegisterProductCommand $command, array $metadata) : self
92
96
{
93
-
return new self($command->getProductId(), $command->getCost(), $metadata["userId"]);
97
+
return new self(
98
+
$command->getProductId(),
99
+
$command->getCost(),
100
+
// all metadata is available for us.
101
+
// Ecotone automatically inject it, if second param is array
102
+
$metadata["userId"]
103
+
);
94
104
}
95
105
```
96
106
97
-
We have added second parameter `$metadata` to our `@CommandHandler`. `Ecotone` read parameters and evaluated what should be injected. We will see soon, how can we take control of this process. \
107
+
We have added second parameter **$metadata** to our **CommandHandler**. **Ecotone** read parameters and evaluate what should be injected. We will see soon, how can we take control of this process. \
98
108
\
99
-
We can add `changePrice` method now.
109
+
We can add **changePrice** method now to our Aggregate:
100
110
101
111
```php
102
112
#[CommandHandler("product.changePrice")]
@@ -110,7 +120,7 @@ public function changePrice(ChangePriceCommand $command, array $metadata) : void
110
120
}
111
121
```
112
122
113
-
And let's call it with incorrect `userId` and see, if we get the exception.
123
+
And let's call it with incorrect **userId** and see, if we get the exception.
114
124
115
125
```php
116
126
public function run() : void
@@ -150,11 +160,11 @@ InvalidArgumentException
150
160
151
161
### Method Invocation
152
162
153
-
We have been just informed, that customers are registering new products in our system. \
154
-
\
155
-
`Only administrator should be allowed to register new product`
163
+
We have been just informed, that customers are registering new products in our system, which should not be a case. Therefore our next requirement is:
156
164
157
-
Let's create simple `UserService` which will tell us, if specific user is administrator. \
165
+
> Only administrator should be allowed to register new Product
166
+
167
+
Let's create simple **UserService** which will tell us, if specific user is administrator. \
158
168
In our testing scenario we will suppose, that only user with `id` of 1 is administrator. 
159
169
160
170
```php
@@ -169,13 +179,18 @@ class UserService
169
179
}
170
180
```
171
181
172
-
Now we need to think where we should call our `UserService.`\
173
-
The good place for it, would not allow for any invocation of `product.register command` without being administrator, otherwise our constraint may be bypassed.\
174
-
`Ecotone` does allow for auto-wire like injection for endpoints. All services registered in Depedency Container are available.
182
+
Now we need to think where we should call our **UserService**.\
183
+
The good place for it, would not allow for any invocation of **product.register** command without being administrator, otherwise our constraint may be bypassed.\
184
+
**Ecotone** does allow for auto-wire like injection for endpoints. All services registered in Depedency Container are available.
175
185
176
186
```php
177
187
#[CommandHandler("product.register")]
178
-
public static function register(RegisterProductCommand $command, array $metadata, UserService $userService) : self
188
+
public static function register(
189
+
RegisterProductCommand $command,
190
+
array $metadata,
191
+
// Any non first class argument, will be considered an DI Service to inject
192
+
UserService $userService
193
+
) : self
179
194
{
180
195
$userId = $metadata["userId"];
181
196
if (!$userService->isAdmin($userId)) {
@@ -186,7 +201,7 @@ public static function register(RegisterProductCommand $command, array $metadata
186
201
}
187
202
```
188
203
189
-
Great, there is no way to bypass the constraint now. The `isAdmin constraint` must be satisfied in order to register new product. \
204
+
Great, there is no way to bypass the constraint now. The **isAdmin constraint** must be satisfied in order to register new product. \
190
205
\
191
206
Let's correct our testing class.  
192
207
@@ -229,11 +244,11 @@ Good job, scenario ran with success!
229
244
230
245
### Injecting arguments
231
246
232
-
`Ecotone` inject arguments based on `Parameter Converters`.\
233
-
Parameter converters , tells `Ecotone` how to resolve specific parameter and what kind of argument it is expecting. The one used for injecting services like `UserService` is `Reference` parameter converter.\
234
-
Let's see how could we use it in our `product.register` command handler. 
247
+
**Ecotone** inject arguments based on **Parameter Converters**.\
248
+
Parameter converters , tells **Ecotone** how to resolve specific parameter and what kind of argument it is expecting. The one used for injecting services like **UserService** is **Reference** parameter converter.\
249
+
Let's see how could we use it in our **product.register** command handler. 
235
250
236
-
Let's suppose UserService is registered under `user-service` in Dependency Container. Then we would need to set up the `CommandHandler`like below.
251
+
Let's suppose UserService is registered under **user-service** in Dependency Container. Then we would need to set up the `CommandHandler`like below.
237
252
238
253
```php
239
254
#[CommandHandler("product.register")]
@@ -244,7 +259,7 @@ public static function register(
244
259
) : self
245
260
```
246
261
247
-
`Reference`- Does inject service from Dependency Container. If `referenceName,` which is name of the service in the container is not given, then it will take the class name as default.
262
+
`Reference`- Does inject service from Dependency Container. If **referenceName**`,` which is name of the service in the container is not given, then it will take the class name as default.
248
263
249
264
`Payload` - Does inject payload of the [message](../messaging/messaging-concepts/message.md). In our case it will be the command itself
250
265
@@ -258,10 +273,11 @@ You may read more detailed description in [Method Invocation section.](../messag
258
273
259
274
### Default Converters
260
275
261
-
`Ecotone`, if parameter converters are not passed provides default converters. \
262
-
First parameter is always `@Payload.`\
263
-
The second parameter, if is `array` then `@Headers` converter is taken, otherwise if class type hint is provided for parameter, then `@Reference` converter is picked. \
264
-
If we would want to manually configure parameters for `product.register` Command Handler, then it would look like this:
276
+
**Ecotone**, if parameter converters are not passed provides default converters. \
277
+
First parameter is always **Payload**`.`\
278
+
The second parameter, if is **array** then **Headers** converter is taken, otherwise if class type hint is provided for parameter, then **Reference** converter is picked. \
279
+
\
280
+
If we would want to manually configure parameters for **product.register** Command Handler, then it would look like this:
265
281
266
282
```php
267
283
#[CommandHandler("product.register")]
@@ -271,19 +287,29 @@ public static function register(
271
287
#[Reference] UserService $userService
272
288
) : self
273
289
{
274
-
$userId = $metadata["userId"];
275
-
if (!$userService->isAdmin($userId)) {
276
-
throw new \InvalidArgumentException("You need to be administrator in order to register new product");
277
-
}
290
+
// ...
291
+
}
292
+
```
293
+
294
+
We could also inject specific header and let Ecotone convert it directly to specific object (if we have Converter registered):
278
295
279
-
return new self($command->getProductId(), $command->getCost(), $metadata["userId"]);
296
+
```php
297
+
#[CommandHandler("product.register")]
298
+
public static function register(
299
+
#[Payload] RegisterProductCommand $command,
300
+
// injecting specific header and doing the conversion string to UserId
301
+
#[Header("userId")] UserId $metadata,
302
+
#[Reference] UserService $userService
303
+
) : self
304
+
{
305
+
// ...
280
306
}
281
307
```
282
308
283
309
{% hint style="success" %}
284
310
Great, we have just finished Lesson 4!
285
311
286
-
In this Lesson we learned about using Meta Data to provide extra information to our Message.\
312
+
In this Lesson we learned about using Metadata to provide extra information to our Message.\
287
313
Besides we took a look on how arguments are injected into endpoint and how we can make use of it.\
288
314
\
289
315
Now we will learn about powerful Interceptors, which can be describes as Middlewares on steroids.
0 commit comments