Skip to content

Commit e80f22a

Browse files
dgafkagitbook-bot
authored andcommitted
GITBOOK-872: No subject
1 parent c6420f1 commit e80f22a

File tree

6 files changed

+434
-240
lines changed

6 files changed

+434
-240
lines changed

SUMMARY.md

+3
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
* [Extending Messaging (Middlewares)](modelling/extending-messaging-middlewares/README.md)
4747
* [Message Headers](modelling/extending-messaging-middlewares/message-headers.md)
4848
* [Interceptors (Middlewares)](modelling/extending-messaging-middlewares/interceptors.md)
49+
* [Extending Message Buses (Gateways)](modelling/extending-messaging-middlewares/extending-message-buses-gateways.md)
4950
* [Event Sourcing](modelling/event-sourcing/README.md)
5051
* [Installation](modelling/event-sourcing/installation.md)
5152
* [Event Sourcing Introduction](modelling/event-sourcing/event-sourcing-introduction/README.md)
@@ -80,6 +81,8 @@
8081
* [Message Handling Isolation](modelling/recovering-tracing-and-monitoring/message-handling-isolation.md)
8182
* [Ecotone Pulse (Service Dashboard)](modelling/recovering-tracing-and-monitoring/ecotone-pulse-service-dashboard.md)
8283
* [Asynchronous Handling and Scheduling](modelling/asynchronous-handling/README.md)
84+
* [Asynchronous Message Handlers](modelling/asynchronous-handling/asynchronous-message-handlers.md)
85+
* [Asynchronous Message Bus (Gateways)](modelling/asynchronous-handling/asynchronous-message-bus-gateways.md)
8386
* [Delaying Messages](modelling/asynchronous-handling/delaying-messages.md)
8487
* [Time to Live](modelling/asynchronous-handling/time-to-live.md)
8588
* [Message Priority](modelling/asynchronous-handling/message-priority.md)

messaging/service-application-configuration.md

+6
Original file line numberDiff line numberDiff line change
@@ -100,3 +100,9 @@ class MyConfiguration
100100
}
101101
}
102102
```
103+
104+
{% hint style="warning" %}
105+
Service Context is evaluated before container is dumped and cached. Therefore if you will change environment variables after your cache is dumped this won't be changed.\
106+
\
107+
This happens because Ecotone tries to maximalize configuration caching, in order to speed up run time execution and do no configuration at that time.
108+
{% endhint %}

modelling/asynchronous-handling/README.md

+12-240
Original file line numberDiff line numberDiff line change
@@ -4,249 +4,21 @@ description: Asynchronous PHP
44

55
# Asynchronous Handling and Scheduling
66

7-
## Running Asynchronously
7+
{% content-ref url="asynchronous-message-handlers.md" %}
8+
[asynchronous-message-handlers.md](asynchronous-message-handlers.md)
9+
{% endcontent-ref %}
810

9-
`Ecotone` does allow for easy change from synchronous to asynchronous execution of given `Message Handler`.
11+
{% content-ref url="asynchronous-message-bus-gateways.md" %}
12+
[asynchronous-message-bus-gateways.md](asynchronous-message-bus-gateways.md)
13+
{% endcontent-ref %}
1014

11-
In order to run Command Handler asynchronously we need to mark it as `Asynchronous`.
15+
{% content-ref url="scheduling.md" %}
16+
[scheduling.md](scheduling.md)
17+
{% endcontent-ref %}
1218

13-
```php
14-
#[Asynchronous("orders")]
15-
#[CommandHandler("order.place", "place_order_endpoint")
16-
public function placeOrder(PlaceOrderCommand $command) : void
17-
{
18-
// do something with $command
19-
}
20-
```
21-
22-
The same way we define for Event Handlers:
23-
24-
```php
25-
#[Asynchronous("orders")]
26-
#[EventHandler(endpointId: "order_was_placed")
27-
public function when(OrderWasPlaced $event) : void
28-
{
29-
// do something with $event
30-
}
31-
```
32-
33-
We need to add **endpointId** on our endpoint's annotation, this will be used to route the Message in isolation to our **Message Handlers**.
34-
35-
## Message Channel Name
36-
37-
```php
38-
#[Asynchronous("orders")]
39-
```
40-
41-
The "orders" string is actually a name of our Message Channel. This way we reference to specific implementation which we would like to use. To provide specific implementation like for example Database Channel, we would use **ServiceContext**.
42-
43-
```php
44-
final readonly class EcotoneConfiguration
45-
{
46-
#[ServiceContext]
47-
public function databaseChannel()
48-
{
49-
return DbalBackedMessageChannelBuilder::create('orders');
50-
}
51-
}
52-
```
53-
54-
This is basically all we need to configure. Now database channel called **orders** will be used, whenever we will use Attribute with this name.
55-
56-
There are multiple different implementation which we can use:
57-
58-
## Available Asynchronous Message Channels
59-
60-
At this moment following modules with pollable channels are available:
61-
62-
* [AMQP Support (RabbitMQ)](../../modules/amqp-support-rabbitmq.md#message-channel)
63-
* [DBAL Support](../../modules/dbal-support.md#message-channel)
64-
* [SQS Support](../../modules/amazon-sqs-support.md)
65-
* [Redis Support](../../modules/redis-support.md)
66-
* [Symfony Messenger Transport Support](../../modules/symfony/symfony-messenger-transport.md)
67-
* [Laravel Queues](../../modules/laravel/laravel-queues.md)
68-
69-
{% hint style="success" %}
70-
If you're choose [Dbal Message Channel](../../modules/dbal-support.md#message-channel) `Ecotone` will use [Outbox Pattern](../../quick-start-php-ddd-cqrs-event-sourcing/resiliency-and-error-handling.md) to atomically store your changes and published messages.
71-
{% endhint %}
72-
73-
{% hint style="info" %}
74-
Currently available Message Channels are integrated via great library [enqueue](https://github.com/php-enqueue/enqueue).
75-
{% endhint %}
76-
77-
{% tabs %}
78-
{% tab title="Symfony" %}
79-
```php
80-
bin/console ecotone:list
81-
+--------------------+
82-
| Endpoint Names |
83-
+--------------------+
84-
| orders |
85-
+--------------------+
86-
```
87-
{% endtab %}
88-
89-
{% tab title="Laravel" %}
90-
```php
91-
artisan ecotone:list
92-
+--------------------+
93-
| Endpoint Names |
94-
+--------------------+
95-
| orders |
96-
+--------------------+
97-
```
98-
{% endtab %}
99-
100-
{% tab title="Lite" %}
101-
```
102-
$consumers = $messagingSystem->list()
103-
```
104-
{% endtab %}
105-
{% endtabs %}
106-
107-
After setting up Pollable Channel we can run the endpoint:
108-
109-
{% tabs %}
110-
{% tab title="Symfony" %}
111-
```php
112-
bin/console ecotone:run orders -vvv
113-
```
114-
{% endtab %}
115-
116-
{% tab title="Laravel" %}
117-
```php
118-
artisan ecotone:run orders -vvv
119-
```
120-
{% endtab %}
121-
122-
{% tab title="Lite" %}
123-
```php
124-
$messagingSystem->run("orders");
125-
```
126-
{% endtab %}
127-
{% endtabs %}
128-
129-
## Running Asychronous Endpoint (PollingMetadata)
130-
131-
### Dynamic Configuration
132-
133-
You may set up running configuration for given consumer while running it.
134-
135-
* `handledMessageLimit` - Amount of messages to be handled before stopping consumer
136-
* `executionTimeLimit` - How long consumer should run before stopping (milliseconds)
137-
* `finishWhenNoMessages` - Consumers will be running as long as there will be messages to consume
138-
* `memoryLimit` - How much memory can be consumed by before stopping consumer (Megabytes)
139-
* `stopOnFailure` - Stop consumer in case of exception
140-
141-
{% tabs %}
142-
{% tab title="Symfony" %}
143-
```php
144-
bin/console ecotone:run orders
145-
--handledMessageLimit=5
146-
--executionTimeLimit=1000
147-
--finishWhenNoMessages
148-
--memoryLimit=512
149-
--stopOnFailure
150-
```
151-
{% endtab %}
152-
153-
{% tab title="Laravel" %}
154-
```php
155-
artisan ecotone:run orders
156-
--handledMessageLimit=5
157-
--executionTimeLimit=1000
158-
--finishWhenNoMessages
159-
--memoryLimit=512
160-
--stopOnFailure
161-
```
162-
{% endtab %}
163-
164-
{% tab title="Lite" %}
165-
```php
166-
$messagingSystem->run(
167-
"orders",
168-
ExecutionPollingMetadata::createWithDefault()
169-
->withHandledMessageLimit(5)
170-
->withMemoryLimitInMegabytes(100)
171-
->withExecutionTimeLimitInMilliseconds(1000)
172-
->withFinishWhenNoMessages(true)
173-
->withStopOnError(true)
174-
);
175-
```
176-
{% endtab %}
177-
{% endtabs %}
178-
179-
### Static Configuration
180-
181-
Using [Service Context ](../../messaging/service-application-configuration.md)configuration for statically configuration.
182-
183-
```php
184-
class Configuration
185-
{
186-
#[ServiceContext]
187-
public function configuration() : array
188-
{
189-
return [
190-
PollingMetadata::create("orders")
191-
->setErrorChannelName("errorChannel")
192-
->setInitialDelayInMilliseconds(100)
193-
->setMemoryLimitInMegaBytes(100)
194-
->setHandledMessageLimit(10)
195-
->setExecutionTimeLimitInMilliseconds(100)
196-
->withFinishWhenNoMessages(true)
197-
];
198-
}
199-
}
200-
```
201-
202-
{% hint style="info" %}
203-
Dynamic configuration overrides static
204-
{% endhint %}
205-
206-
## Multiple Asynchronous Endpoints
207-
208-
Using single asynchronous channel we may register multiple endpoints. \
209-
This allow for registering single asynchronous channel for whole Aggregate or group of related Command/Event Handlers. 
210-
211-
```php
212-
#[Asynchronous("orders")]
213-
#[EventHandler]
214-
public function onSuccess(SuccessEvent $event) : void
215-
{
216-
}
217-
218-
#[Asynchronous("orders")]
219-
#[EventHandler]
220-
public function onSuccess(FailureEvent $event) : void
221-
{
222-
}
223-
```
224-
225-
#### Asynchronous Class
226-
227-
You may put `Asynchronous` on the class, level so all the endpoints within a class will becomes asynchronous.
228-
229-
## Intercepting asynchronous endpoint
230-
231-
All asynchronous endpoints are marked with special attribute`Ecotone\Messaging\Attribute\AsynchronousRunningEndpoint` \
232-
If you want to [intercept](../extending-messaging-middlewares/interceptors.md) all polling endpoints you should make use of [annotation related point cut](../extending-messaging-middlewares/interceptors.md#pointcut) on this.
233-
234-
## Endpoint Id
235-
236-
Each Asynchronous Message Handler requires us to define **"endpointId"**. It's unique identifier of your Message Handler.
237-
238-
```php
239-
#[Asynchronous("orders")]
240-
#[EventHandler(endpointId: "order_was_placed") // Your important endpoint Id
241-
public function when(OrderWasPlaced $event) : void {}
242-
```
243-
244-
Endpoint Id goes in form of Headers to your Message Queue. After Message is consumed from the Queue, Message will be directed to your Message Handler having given endpoint Id. \
245-
This decouples the Message completely from the Message Handler Class and Method and Command/Event Class. 
246-
247-
{% hint style="success" %}
248-
EndpointId ensures we can freely refactor our code and it will be backward compatible with Messages in the Queues. This means we can move the method and class to different namespaces, change the Command class names and as long as **endpointId** is kept the same Message will be delivered correctly. 
249-
{% endhint %}
19+
{% content-ref url="dynamic-message-channels.md" %}
20+
[dynamic-message-channels.md](dynamic-message-channels.md)
21+
{% endcontent-ref %}
25022

25123
## Materials
25224

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# Asynchronous Message Bus (Gateways)
2+
3+
We may extend Gateways functionality with asynchronocity. This way we can pass any Message via given Message Channel first.
4+
5+
{% hint style="success" %}
6+
Asynchronous Gateways are available as part of **Ecotone Enterprise.**
7+
{% endhint %}
8+
9+
## Asynchronous Gateways
10+
11+
To make Gateway Asynchronous we will use Asynchronous Attribute, just like with Asynchronous Message Handlers. We may can extend any types of Gateways: Command/Event/Query Buses, [Business Interfaces](../command-handling/business-interface/) or custom [Gateways](../../messaging/messaging-concepts/messaging-gateway.md).
12+
13+
To build for example a **CommandBus** which will send Messages over **async** channel, we will simply [extend a CommandBus interface](../extending-messaging-middlewares/extending-message-buses-gateways.md), and add our method.
14+
15+
```php
16+
#[Asynchronous("async")]
17+
interface AsynchronousCommandBus extends CommandBus
18+
{
19+
20+
}
21+
```
22+
23+
then we all Commands that will be triggered via **AsynchronousCommandBus** will go over async channel. 
24+
25+
{% hint style="success" %}
26+
It's enough to extend given **CommandBus** with custom interface to register new abstraction in Gateway in Dependency Container. 
27+
{% endhint %}
28+
29+
Having **asynchronous CommandBus** is especially useful, if given Message Handler is not meant be executed asynchronous by default.
30+
31+
```php
32+
#[CommandHandler]
33+
public function placeOrder(PlaceOrderCommand $command) : void
34+
{
35+
// do something with $command
36+
}
37+
```
38+
39+
then when using standard CommandBus above Command Handler will be executed synchronous, when using AsynchronousCommandBus it will be done asynchronously.
40+
41+
## Outbox Event Bus
42+
43+
It's easy to build an Outbox pattern using this Asynchronous Gateways. Just make use of[ Dbal Message Channel](../../modules/dbal-support.md) to push Messages over Database Channel. 
44+
45+
```php
46+
#[Asynchronous("outbox")]
47+
interface OutboxEventBus extends EventBus {}
48+
```
49+
50+
and then register dbal channel
51+
52+
```php
53+
#[ServiceContext]
54+
public function orderChannel()
55+
{
56+
return DbalBackedMessageChannelBuilder::create("orders");
57+
}
58+
```
59+
60+
Then whenever we will send Events within Command Handler (which is wrapped in transaction by default while using Dbal Module). Messages will be commited as part of same transaction.
61+
62+
```php
63+
#[CommandHandler]
64+
public function placeOrder(PlaceOrderCommand $command, OutboxEventBus $eventBus) : void
65+
{
66+
// do something with $command
67+
68+
$eventBus->publish(new OrderWasPlaced());
69+
}
70+
```

0 commit comments

Comments
 (0)