Skip to content

Commit 11c12e0

Browse files
dgafkagitbook-bot
authored andcommitted
GITBOOK-865: No subject
1 parent 421cb1c commit 11c12e0

File tree

2 files changed

+75
-48
lines changed

2 files changed

+75
-48
lines changed

tutorial-php-ddd-cqrs-event-sourcing/README.md

+9-8
Original file line numberDiff line numberDiff line change
@@ -6,24 +6,25 @@ description: Ecotone PHP Framework
66

77
## Get started with Ecotone
88

9-
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.\
1010
Therefore we will build a small back-end for Shopping System during this tutorial. \
1111
The techniques we will learn in the tutorial are **fundamental to building any application using Ecotone**. 
1212

1313
{% 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).
1516
{% endhint %}
1617

1718
## Lessons
1819

1920
The tutorial is divided into several lessons:
2021

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 **Tactical Domain 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.
2728

2829
{% hint style="success" %}
2930
You don’t have to complete all of the lessons at once to get the value out of this tutorial. \

tutorial-php-ddd-cqrs-event-sourcing/php-metadata-method-invocation.md

+66-40
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,18 @@ Not having code for _Lesson 4?_ \
1212

1313
### Metadata
1414

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**. 
1716

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.:
1923

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. 
2125
22-
Let's start by adding `ChangePriceCommand.`
26+
Let's start by adding **ChangePriceCommand**
2327

2428
```php
2529
namespace App\Domain\Product;
@@ -42,8 +46,8 @@ class ChangePriceCommand
4246
}
4347
```
4448

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.
4751

4852
```php
4953
public function run() : void
@@ -52,7 +56,7 @@ public function run() : void
5256
"product.register",
5357
\json_encode(["productId" => 1, "cost" => 100]),
5458
"application/json",
55-
[
59+
metadata: [
5660
"userId" => 1
5761
]
5862
);
@@ -61,17 +65,17 @@ public function run() : void
6165
}
6266
```
6367

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: 
6771

6872
```php
6973
#[Aggregate]
7074
class Product
7175
{
7276
use WithAggregateEvents;
7377

74-
#[AggregateIdentifier]
78+
#[Identifier]
7579
private int $productId;
7680

7781
private Cost $cost;
@@ -90,13 +94,19 @@ class Product
9094
#[CommandHandler("product.register")]
9195
public static function register(RegisterProductCommand $command, array $metadata) : self
9296
{
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+
);
94104
}
95105
```
96106

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. \
98108
\
99-
We can add `changePrice` method now.
109+
We can add **changePrice** method now to our Aggregate:
100110

101111
```php
102112
#[CommandHandler("product.changePrice")]
@@ -110,7 +120,7 @@ public function changePrice(ChangePriceCommand $command, array $metadata) : void
110120
}
111121
```
112122

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.
114124

115125
```php
116126
public function run() : void
@@ -150,11 +160,11 @@ InvalidArgumentException
150160

151161
### Method Invocation
152162

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:
156164

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. \
158168
In our testing scenario we will suppose, that only user with `id` of 1 is administrator. 
159169

160170
```php
@@ -169,13 +179,18 @@ class UserService
169179
}
170180
```
171181

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.
175185

176186
```php
177187
#[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
179194
{
180195
$userId = $metadata["userId"];
181196
if (!$userService->isAdmin($userId)) {
@@ -186,7 +201,7 @@ public static function register(RegisterProductCommand $command, array $metadata
186201
}
187202
```
188203

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. \
190205
\
191206
Let's correct our testing class.  
192207

@@ -229,11 +244,11 @@ Good job, scenario ran with success!
229244

230245
### Injecting arguments
231246

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. 
235250

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.
237252

238253
```php
239254
#[CommandHandler("product.register")]
@@ -244,7 +259,7 @@ public static function register(
244259
) : self
245260
```
246261

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.
248263

249264
`Payload` - Does inject payload of the [message](../messaging/messaging-concepts/message.md). In our case it will be the command itself
250265

@@ -258,10 +273,11 @@ You may read more detailed description in [Method Invocation section.](../messag
258273

259274
### Default Converters
260275

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:
265281

266282
```php
267283
#[CommandHandler("product.register")]
@@ -271,19 +287,29 @@ public static function register(
271287
#[Reference] UserService $userService
272288
) : self
273289
{
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):
278295

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+
// ...
280306
}
281307
```
282308

283309
{% hint style="success" %}
284310
Great, we have just finished Lesson 4!
285311

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.\
287313
Besides we took a look on how arguments are injected into endpoint and how we can make use of it.\
288314
\
289315
Now we will learn about powerful Interceptors, which can be describes as Middlewares on steroids.

0 commit comments

Comments
 (0)