diff --git a/2022-02/spring-29-integration-example/pom.xml b/2022-02/spring-29-integration-example/pom.xml
new file mode 100644
index 00000000..059e0ccf
--- /dev/null
+++ b/2022-02/spring-29-integration-example/pom.xml
@@ -0,0 +1,41 @@
+
+
+ 4.0.0
+
+ ru.otus
+ spring-29-integration-example
+ 1.0-SNAPSHOT
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.3.3.RELEASE
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-integration
+
+
+ org.springframework
+ spring-messaging
+
+
+ org.apache.commons
+ commons-lang3
+ 3.7
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
diff --git a/2022-02/spring-29-integration-example/src/main/java/ru/otus/spring/integration/App.java b/2022-02/spring-29-integration-example/src/main/java/ru/otus/spring/integration/App.java
new file mode 100644
index 00000000..fe2ccde0
--- /dev/null
+++ b/2022-02/spring-29-integration-example/src/main/java/ru/otus/spring/integration/App.java
@@ -0,0 +1,21 @@
+package ru.otus.spring.integration;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+import org.springframework.context.support.AbstractApplicationContext;
+import ru.otus.spring.integration.service.OrderService;
+
+@SpringBootApplication
+public class App {
+
+ public static void main( String[] args ) throws Exception {
+ ConfigurableApplicationContext ctx = SpringApplication.run(App.class, args);
+
+ OrderService orderService = ctx.getBean(OrderService.class);
+ orderService.startOrdersLoop();
+
+ }
+
+}
diff --git a/2022-02/spring-29-integration-example/src/main/java/ru/otus/spring/integration/domain/Food.java b/2022-02/spring-29-integration-example/src/main/java/ru/otus/spring/integration/domain/Food.java
new file mode 100644
index 00000000..16d8e9c6
--- /dev/null
+++ b/2022-02/spring-29-integration-example/src/main/java/ru/otus/spring/integration/domain/Food.java
@@ -0,0 +1,15 @@
+package ru.otus.spring.integration.domain;
+
+
+public class Food {
+
+ private final String name;
+
+ public Food(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+}
diff --git a/2022-02/spring-29-integration-example/src/main/java/ru/otus/spring/integration/domain/OrderItem.java b/2022-02/spring-29-integration-example/src/main/java/ru/otus/spring/integration/domain/OrderItem.java
new file mode 100644
index 00000000..68612e96
--- /dev/null
+++ b/2022-02/spring-29-integration-example/src/main/java/ru/otus/spring/integration/domain/OrderItem.java
@@ -0,0 +1,14 @@
+package ru.otus.spring.integration.domain;
+
+public class OrderItem {
+
+ private final String itemName;
+
+ public OrderItem(String itemName) {
+ this.itemName = itemName;
+ }
+
+ public String getItemName() {
+ return itemName;
+ }
+}
diff --git a/2022-02/spring-29-integration-example/src/main/java/ru/otus/spring/integration/integration/Cafe.java b/2022-02/spring-29-integration-example/src/main/java/ru/otus/spring/integration/integration/Cafe.java
new file mode 100644
index 00000000..d3983bf9
--- /dev/null
+++ b/2022-02/spring-29-integration-example/src/main/java/ru/otus/spring/integration/integration/Cafe.java
@@ -0,0 +1,16 @@
+package ru.otus.spring.integration.integration;
+
+
+import org.springframework.integration.annotation.Gateway;
+import org.springframework.integration.annotation.MessagingGateway;
+import ru.otus.spring.integration.domain.Food;
+import ru.otus.spring.integration.domain.OrderItem;
+
+import java.util.Collection;
+
+@MessagingGateway
+public interface Cafe {
+
+ @Gateway(requestChannel = "itemsChannel", replyChannel = "foodChannel")
+ Collection process(Collection orderItem);
+}
diff --git a/2022-02/spring-29-integration-example/src/main/java/ru/otus/spring/integration/integration/IntegrationConfig.java b/2022-02/spring-29-integration-example/src/main/java/ru/otus/spring/integration/integration/IntegrationConfig.java
new file mode 100644
index 00000000..17cfa73c
--- /dev/null
+++ b/2022-02/spring-29-integration-example/src/main/java/ru/otus/spring/integration/integration/IntegrationConfig.java
@@ -0,0 +1,45 @@
+package ru.otus.spring.integration.integration;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.integration.channel.PublishSubscribeChannel;
+import org.springframework.integration.channel.QueueChannel;
+import org.springframework.integration.dsl.IntegrationFlow;
+import org.springframework.integration.dsl.IntegrationFlows;
+import org.springframework.integration.dsl.MessageChannels;
+import org.springframework.integration.dsl.Pollers;
+import org.springframework.integration.scheduling.PollerMetadata;
+import ru.otus.spring.integration.service.KitchenService;
+
+@Configuration
+public class IntegrationConfig {
+
+ private static final int QUEUE_CAPACITY = 10;
+ private static final String COOK_METHOD_NAME = "cook";
+
+ @Bean
+ public QueueChannel itemsChannel() {
+ return MessageChannels.queue(QUEUE_CAPACITY).get();
+ }
+
+ @Bean
+ public PublishSubscribeChannel foodChannel() {
+ return MessageChannels.publishSubscribe().get();
+ }
+
+ @Bean(name = PollerMetadata.DEFAULT_POLLER)
+ public PollerMetadata poller() {
+ return Pollers.fixedRate(100).maxMessagesPerPoll(2).get();
+ }
+
+ @Bean
+ public IntegrationFlow cafeFlow(KitchenService kitchenService) {
+ return IntegrationFlows.from(itemsChannel())
+ .split()
+ .handle(kitchenService, COOK_METHOD_NAME)
+ .aggregate()
+ .channel(foodChannel())
+ .get();
+ }
+
+}
diff --git a/2022-02/spring-29-integration-example/src/main/java/ru/otus/spring/integration/service/KitchenService.java b/2022-02/spring-29-integration-example/src/main/java/ru/otus/spring/integration/service/KitchenService.java
new file mode 100644
index 00000000..772151b6
--- /dev/null
+++ b/2022-02/spring-29-integration-example/src/main/java/ru/otus/spring/integration/service/KitchenService.java
@@ -0,0 +1,8 @@
+package ru.otus.spring.integration.service;
+
+import ru.otus.spring.integration.domain.Food;
+import ru.otus.spring.integration.domain.OrderItem;
+
+public interface KitchenService {
+ Food cook(OrderItem orderItem) throws Exception;
+}
diff --git a/2022-02/spring-29-integration-example/src/main/java/ru/otus/spring/integration/service/KitchenServiceImpl.java b/2022-02/spring-29-integration-example/src/main/java/ru/otus/spring/integration/service/KitchenServiceImpl.java
new file mode 100644
index 00000000..2f7fcc08
--- /dev/null
+++ b/2022-02/spring-29-integration-example/src/main/java/ru/otus/spring/integration/service/KitchenServiceImpl.java
@@ -0,0 +1,17 @@
+package ru.otus.spring.integration.service;
+
+import org.springframework.stereotype.Service;
+import ru.otus.spring.integration.domain.Food;
+import ru.otus.spring.integration.domain.OrderItem;
+
+@Service
+public class KitchenServiceImpl implements KitchenService {
+
+ @Override
+ public Food cook(OrderItem orderItem) throws Exception {
+ System.out.println("Cooking " + orderItem.getItemName());
+ Thread.sleep(3000);
+ System.out.println("Cooking " + orderItem.getItemName() + " done");
+ return new Food(orderItem.getItemName());
+ }
+}
diff --git a/2022-02/spring-29-integration-example/src/main/java/ru/otus/spring/integration/service/OrderService.java b/2022-02/spring-29-integration-example/src/main/java/ru/otus/spring/integration/service/OrderService.java
new file mode 100644
index 00000000..21cf5657
--- /dev/null
+++ b/2022-02/spring-29-integration-example/src/main/java/ru/otus/spring/integration/service/OrderService.java
@@ -0,0 +1,5 @@
+package ru.otus.spring.integration.service;
+
+public interface OrderService {
+ void startOrdersLoop() throws Exception;
+}
diff --git a/2022-02/spring-29-integration-example/src/main/java/ru/otus/spring/integration/service/OrderServiceImpl.java b/2022-02/spring-29-integration-example/src/main/java/ru/otus/spring/integration/service/OrderServiceImpl.java
new file mode 100644
index 00000000..8c6e3fde
--- /dev/null
+++ b/2022-02/spring-29-integration-example/src/main/java/ru/otus/spring/integration/service/OrderServiceImpl.java
@@ -0,0 +1,60 @@
+package ru.otus.spring.integration.service;
+
+import org.apache.commons.lang3.RandomUtils;
+import org.springframework.stereotype.Service;
+import ru.otus.spring.integration.domain.Food;
+import ru.otus.spring.integration.domain.OrderItem;
+import ru.otus.spring.integration.integration.Cafe;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.ForkJoinPool;
+import java.util.stream.Collectors;
+
+@Service
+public class OrderServiceImpl implements OrderService {
+
+ private static final int ORDERS_DELAY_MILLS = 7000;
+ private static final String[] MENU = {"coffee", "tea", "smoothie", "whiskey", "beer", "cola", "water"};
+
+ private final Cafe cafe;
+
+ public OrderServiceImpl(Cafe cafe) {
+ this.cafe = cafe;
+ }
+
+ @SuppressWarnings({"resource", "Duplicates", "InfiniteLoopStatement", "BusyWait"})
+ @Override
+ public void startOrdersLoop() throws Exception {
+ ForkJoinPool pool = ForkJoinPool.commonPool();
+
+ while (true) {
+ Thread.sleep(ORDERS_DELAY_MILLS);
+
+ pool.execute(() -> {
+ Collection items = generateOrderItems();
+ System.out.println("New orderItems: " +
+ items.stream().map(OrderItem::getItemName)
+ .collect(Collectors.joining(",")));
+ Collection food = cafe.process(items);
+ System.out.println("Ready food: " + food.stream()
+ .map(Food::getName)
+ .collect(Collectors.joining(",")));
+ });
+ }
+ }
+
+
+ private OrderItem generateOrderItem() {
+ return new OrderItem(MENU[RandomUtils.nextInt(0, MENU.length)]);
+ }
+
+ private Collection generateOrderItems() {
+ List items = new ArrayList<>();
+ for (int i = 0; i < RandomUtils.nextInt(1, 5); ++i) {
+ items.add(generateOrderItem());
+ }
+ return items;
+ }
+}