$30 off During Our Annual Pro Sale. View Details »

Get Reactive With Project Reactor and Spring 5

Get Reactive With Project Reactor and Spring 5

Spring – the tool that is over 13 years pleases our hearts and eyes of customers. Despite this long history of development the project continues to follow the latest trends and provides to developers the awesome set of add-ons that simplify the daily life.

During this tech-talk, I would like to discuss delicacies which were cooked by developers from Pivotal and how Reactivity reflected on the 5th iteration of the framework.

Oleh Dokuka

May 26, 2017
Tweet

More Decks by Oleh Dokuka

Other Decks in Programming

Transcript

  1. Get Reactive With
    Spring 5
    And
    Project Reactor
    by Oleh Dokuka

    View Slide

  2. • JSE at Levi9
    • 6+ dev experience
    • Spring fan
    • Reactivity preacher
    /oleh.dokuka
    /OlegDokuka
    /OlehDokuka
    WHO AM I?

    View Slide

  3. Agenda
    1. Why Reactive?
    2. Reactivity Generations
    3. Reactive Streams
    4. Project Reactor
    5. Reactivity in Action (Project Reactor + Spring 5)

    View Slide

  4. So, why Reactive?1

    View Slide

  5. Requirements
    of
    Nowadays
    https://sites.psu.edu/luopassion2/2016/01/25/a-brief-history-of-the-internet-of-things-iot/

    View Slide

  6. Lean resource
    management
    https://i.ytimg.com/vi/EiH1sJCDs9c/maxresdefault.jpg

    View Slide

  7. Asynchrony
    http://www.cntraveler.com/story/the-main-reasons-your-passport-application-will-be-denied

    View Slide

  8. Fault Tolerance

    View Slide

  9. Powerful way of
    thinking
    http://www.wallpaperup.com/200710/pipes_cranes.html

    View Slide

  10. Reactive-Manifesto2

    View Slide

  11. Sounds Cool

    View Slide

  12. Reactivity
    Generations
    3

    View Slide

  13. Handler
    Observer
    Java

    View Slide

  14. Skill
    Knowledge
    Rx.NET

    View Slide

  15. Akka
    RxJava

    View Slide

  16. RxJava
    AkkaStreams

    View Slide

  17. RxJava 2
    AkkaStream
    Project
    Reactor
    Reactive Streams Specified

    View Slide

  18. View Slide

  19. Reactive Streams4

    View Slide

  20. What is the Purpose?

    View Slide

  21. Backpressure

    View Slide

  22. View Slide

  23. Common API

    View Slide

  24. Publisher Subscriber

    View Slide

  25. Publisher Subscriber
    public interface Publisher {
    public void subscribe(
    Subscriber super T> s
    );
    }

    View Slide

  26. Publisher Subscriber
    public interface Subscriber {
    public void onSubscribe(Subscription s);
    public void onNext(T t);
    public void onError(Throwable t);
    public void onComplete();
    }

    View Slide

  27. Publisher Subscriber
    Subscription

    View Slide

  28. Publisher Subscriber
    Subscription
    public interface Subscription {
    public void request(long n);
    public void cancel();
    }

    View Slide

  29. Publisher Subscriber
    Subscription
    public interface Subscription {
    public void request(long n);
    public void cancel();
    }

    View Slide

  30. Publisher Subscriber
    Subscription

    View Slide

  31. Processor
    Publisher Subscriber
    Subscription

    View Slide

  32. Publisher Subscriber
    Subscription
    Processor
    public interface Processor
    extends Subscriber, Publisher
    {}

    View Slide

  33. Example

    View Slide

  34. View Slide

  35. View Slide

  36. View Slide

  37. View Slide

  38. View Slide

  39. View Slide

  40. View Slide

  41. View Slide

  42. View Slide

  43. Project Reactor

    View Slide

  44. by

    View Slide

  45. RxJava-like API

    View Slide

  46. Reactive Types

    View Slide

  47. Flux

    View Slide

  48. like
    Stream

    View Slide

  49. View Slide

  50. Mono

    View Slide

  51. like
    CompletableFuture

    View Slide

  52. View Slide

  53. Useful Operators

    View Slide

  54. Operator
    ==
    Very Smart Publisher

    View Slide

  55. merge
    .merge(…)

    View Slide

  56. zip
    .zip(…)

    View Slide

  57. zip
    .zip(…)

    View Slide

  58. publish
    .publish(…)

    View Slide

  59. .materialize(…)
    materialize

    View Slide

  60. materialize
    .materialize(…)

    View Slide

  61. Thread A
    Thread Main
    .publishOn(…)
    publishOn

    View Slide

  62. View Slide

  63. RxJava.observeOn
    ==
    Reactor.publishOn

    View Slide

  64. Remember!

    View Slide

  65. Nothing happens until
    .subscribe ()

    View Slide

  66. Reactivity in Action

    View Slide

  67. What we`re going to do?

    View Slide

  68. Refactoring

    View Slide

  69. What do we have?

    View Slide

  70. Chat Application

    View Slide

  71. Functionality
    • Gitter Chat Communication
    • Messaging Statistic

    View Slide

  72. Demo

    View Slide

  73. View Slide

  74. Toolkit
    • Spring Framework 4
    • Spring Boot
    • Spring MVC
    • Spring Data JPA

    View Slide

  75. Architecture

    View Slide

  76. User Client
    Gitter Service
    Whole Application
    DB Layer
    Service Layer
    Controllers Layer

    View Slide

  77. Controllers Layer

    View Slide

  78. Returns
    Thymeleaf View

    View Slide

  79. REST Resources

    View Slide

  80. Services Layer

    View Slide

  81. External Chats SPI

    View Slide

  82. Domain Services

    View Slide

  83. Gitter integration

    View Slide

  84. Implementation

    View Slide

  85. Persistence Layer

    View Slide

  86. Database Model

    View Slide

  87. How it works?

    View Slide

  88. View Slide

  89. View Slide

  90. View Slide

  91. View Slide

  92. Problems

    View Slide

  93. Problems
    • Pulling model

    View Slide

  94. View Slide

  95. Problems
    • Pulling model
    • Too much blocking I/O

    View Slide

  96. Solutions

    View Slide

  97. Apply
    Reactive Manifesto

    View Slide

  98. View Slide

  99. Async Messaging
    Push Model

    View Slide

  100. Apply
    Streaming API

    View Slide

  101. View Slide

  102. View Slide

  103. Async Messaging
    Elasticity

    View Slide

  104. Dedicate
    ThreadPool

    View Slide

  105. View Slide

  106. View Slide

  107. Async Messaging
    Push Model

    View Slide

  108. Apply WebSocket

    View Slide

  109. View Slide

  110. How to bring it all
    together?

    View Slide

  111. Spring 5

    View Slide

  112. Painless replacement

    View Slide

  113. WebFlux
    @Controller, @RequestMapping,…
    Spring MVC
    Servlet API
    Servlet Container
    Spring Web Reactive
    Reactive HTTP
    Servlet, Netty, Undertow

    View Slide

  114. View Slide

  115. View Slide

  116. @RestController
    @RequestMapping("/api/v1/statistics")
    public class StatisticResource {
    private final StatisticService statisticService;
    @Autowired
    public StatisticResource(
    StatisticService statisticService) {
    this.statisticService = statisticService;
    }
    @GetMapping("/users")
    public List getUsersStatistic() {
    return statisticService.getUsersStatistic();
    }
    }

    View Slide

  117. @RestController
    @RequestMapping("/api/v1/statistics")
    public class StatisticResource {
    private final StatisticService statisticService;
    @Autowired
    public StatisticResource(
    StatisticService statisticService) {
    this.statisticService = statisticService;
    }
    @GetMapping("/users")
    public List getUsersStatistic() {
    return statisticService.getUsersStatistic();
    }
    }

    View Slide

  118. @RestController
    @RequestMapping("/api/v1/statistics")
    public class StatisticResource {
    private final StatisticService statisticService;
    @Autowired
    public StatisticResource(
    StatisticService statisticService) {
    this.statisticService = statisticService;
    }
    @GetMapping("/users")
    public Flux getUsersStatistic() {
    return Flux.fromIterable(statisticService
    .getUsersStatistic());
    }

    View Slide

  119. @RestController
    @RequestMapping("/api/v1/statistics")
    public class StatisticResource {
    private final StatisticService statisticService;
    @Autowired
    public StatisticResource(
    StatisticService statisticService) {
    this.statisticService = statisticService;
    }
    @GetMapping("/users")
    public Flux getUsersStatistic() {
    return Flux.fromIterable(statisticService
    .getUsersStatistic());
    }

    View Slide

  120. Project Reactor + Netty
    Integration

    View Slide

  121. Why Netty?

    View Slide

  122. Non-Blocking,Reactive
    Server

    View Slide

  123. Reactive Architecture

    View Slide

  124. Data Base Connection Pipe
    Gitter Connection Pipe

    View Slide

  125. Refactoring Structure

    View Slide

  126. 1. Blocking I/O Refactoring
    2. Service Refactoring
    3. Endpoints Refactoring
    4. Pipeline Testing

    View Slide

  127. View Slide

  128. Blocking I/O Refactoring

    View Slide

  129. View Slide

  130. Reactive DB Access
    via
    ReactiveCrudRepository

    View Slide

  131. interface ReactiveCrudRepository extends Repository
    Mono save(S entity);
    Flux saveAll(Iterable entities);
    Flux saveAll(Publisher entityStream);
    Mono findById(ID id);
    Mono findById(Mono id);
    Mono existsById(ID id);
    Mono existsById(Mono id);
    Flux findAll();
    Flux findAllById(Iterable ids);
    Flux findAllById(Publisher idStream);
    Mono count();
    Mono deleteById(ID id);
    Mono delete(T entity);
    Mono deleteAll(Iterable extends T> entities);
    Mono deleteAll(Publisher extends T> entityStream);
    Mono deleteAll();
    }

    View Slide

  132. interface ReactiveCrudRepository extends Repository
    Mono save(S entity);
    Flux saveAll(Iterable entities);
    Flux saveAll(Publisher entityStream);
    Mono findById(ID id);
    Mono findById(Mono id);
    Mono existsById(ID id);
    Mono existsById(Mono id);
    Flux findAll();
    Flux findAllById(Iterable ids);
    Flux findAllById(Publisher idStream);
    Mono count();
    Mono deleteById(ID id);
    Mono delete(T entity);
    Mono deleteAll(Iterable extends T> entities);
    Mono deleteAll(Publisher extends T> entityStream);
    Mono deleteAll();
    }

    View Slide

  133. Code session

    View Slide

  134. .publishOn()
    public static Mono mono(
    Mono input,
    Function<...> transformer
    ) {
    MonoProcessor monoProcessor =
    MonoProcessor.create();
    input
    .publishOn(Schedulers.elastic())
    .transform(transformer)
    .subscribe(monoProcessor);
    return monoProcessor
    .subscribeOn(Schedulers.elastic());
    }

    View Slide

  135. .publishOn()
    public static Mono mono(
    Mono input,
    Function<...> transformer
    ) {
    MonoProcessor monoProcessor =
    MonoProcessor.create();
    input
    .publishOn(Schedulers.elastic())
    .transform(transformer)
    .subscribe(monoProcessor);
    return monoProcessor
    .subscribeOn(Schedulers.elastic());
    }

    View Slide

  136. .publishOn()
    public static Mono mono(
    Mono input,
    Function<...> transformer
    ) {
    MonoProcessor monoProcessor =
    MonoProcessor.create();
    input
    .publishOn(Schedulers.elastic())
    .transform(transformer)
    .subscribe(monoProcessor);
    return monoProcessor
    .subscribeOn(Schedulers.elastic());
    }

    View Slide

  137. .publishOn()
    public static Mono mono(
    Mono input,
    Function<...> transformer
    ) {
    MonoProcessor monoProcessor =
    MonoProcessor.create();
    input
    .publishOn(Schedulers.elastic())
    .transform(transformer)
    .subscribe(monoProcessor);
    return monoProcessor
    .subscribeOn(Schedulers.elastic());
    }

    View Slide

  138. .publishOn()
    public static Mono mono(
    Mono input,
    Function<...> transformer
    ) {
    MonoProcessor monoProcessor =
    MonoProcessor.create();
    input
    .publishOn(Schedulers.elastic())
    .transform(transformer)
    .subscribe(monoProcessor);
    return monoProcessor
    .subscribeOn(Schedulers.elastic());
    }

    View Slide

  139. .publishOn()
    public static Mono mono(
    Mono input,
    Function<...> transformer
    ) {
    MonoProcessor monoProcessor =
    MonoProcessor.create();
    input
    .publishOn(Schedulers.elastic())
    .transform(transformer)
    .subscribe(monoProcessor);
    return monoProcessor
    .subscribeOn(Schedulers.elastic());
    }

    View Slide

  140. public static Mono mono(
    Mono input,
    Function<...> transformer
    ) {
    MonoProcessor monoProcessor =
    MonoProcessor.create();
    input
    .publishOn(Schedulers.elastic())
    .transform(transformer)
    .subscribe(monoProcessor);
    return monoProcessor
    .subscribeOn(Schedulers.elastic());
    }
    .publishOn()

    View Slide

  141. public static Mono mono(
    Mono input,
    Function<...> transformer
    ) {
    MonoProcessor monoProcessor =
    MonoProcessor.create();
    input
    .publishOn(Schedulers.elastic())
    .transform(transformer)
    .subscribe(monoProcessor);
    return monoProcessor
    .subscribeOn(Schedulers.elastic());
    }
    .subscribeOn()
    MonoProcessor

    View Slide

  142. public static Mono mono(
    Mono input,
    Function<...> transformer
    ) {
    MonoProcessor monoProcessor =
    MonoProcessor.create();
    input
    .publishOn(Schedulers.elastic())
    .transform(transformer)
    .subscribe(monoProcessor);
    return monoProcessor
    .subscribeOn(Schedulers.elastic());
    }
    .subscribeOn()
    MonoProcessor

    View Slide

  143. public static Mono mono(
    Mono input,
    Function<...> transformer
    ) {
    MonoProcessor monoProcessor =
    MonoProcessor.create();
    input
    .publishOn(Schedulers.elastic())
    .transform(transformer)
    .subscribe(monoProcessor);
    return monoProcessor
    .subscribeOn(Schedulers.elastic());
    }
    MonoProcessor
    .subscribeOn()
    …subscribe();

    View Slide

  144. public static Mono mono(
    Mono input,
    Function<...> transformer
    ) {
    MonoProcessor monoProcessor =
    MonoProcessor.create();
    input
    .publishOn(Schedulers.elastic())
    .transform(transformer)
    .subscribe(monoProcessor);
    return monoProcessor
    .subscribeOn(Schedulers.elastic());
    }
    …subscribe();
    MonoProcessor
    .subscribeOn()
    …subscribe();

    View Slide

  145. Service Refactoring

    View Slide

  146. Gitter Service

    View Slide

  147. Reactive WebClient
    out of the box

    View Slide

  148. public interface WebClient {
    UriSpec> get();
    //omitted...
    static WebClient create() {
    return new DefaultWebClientBuilder().build();
    }
    interface RequestHeadersSpec> {
    //omitted...
    Mono exchange();
    //omitted...
    }
    interface ResponseSpec {
    Mono bodyToMono(Class bodyType);
    Flux bodyToFlux(Class elementType);
    Mono> toEntity(Class bodyType);
    Mono>> toEntityList(Class element
    }
    }

    View Slide

  149. public interface WebClient {
    UriSpec> get();
    //omitted...
    static WebClient create() {
    return new DefaultWebClientBuilder().build();
    }
    interface RequestHeadersSpec> {
    //omitted...
    Mono exchange();
    //omitted...
    }
    interface ResponseSpec {
    Mono bodyToMono(Class bodyType);
    Flux bodyToFlux(Class elementType);
    Mono> toEntity(Class bodyType);
    Mono>> toEntityList(Class element
    }
    }

    View Slide

  150. View Slide

  151. Code Session

    View Slide

  152. @Autowired
    public GitterService(
    GitterClient gitterClient
    ) {
    gitterMessageSource = Flux.from(
    gitterClient
    .getMessages(null)
    )
    .onBackpressureBuffer()
    .publish(1)
    .autoConnect(0);
    }

    View Slide

  153. @Autowired
    public GitterService(
    GitterClient gitterClient
    ) {
    gitterMessageSource = Flux.from(
    gitterClient
    .getMessages(null)
    )
    .onBackpressureBuffer()
    .publish(1)
    .autoConnect(0);
    }
    .onBP()

    View Slide

  154. @Autowired
    public GitterService(
    GitterClient gitterClient
    ) {
    gitterMessageSource = Flux.from(
    gitterClient
    .getMessages(null)
    )
    .onBackpressureBuffer()
    .publish(1)
    .autoConnect(0);
    }
    .onBP()

    View Slide

  155. .onBP()
    @Autowired
    public GitterService(
    GitterClient gitterClient
    ) {
    gitterMessageSource = Flux.from(
    gitterClient
    .getMessages(null)
    )
    .onBackpressureBuffer()
    .publish(1)
    .autoConnect(0);
    }

    View Slide

  156. .onBP()
    @Autowired
    public GitterService(
    GitterClient gitterClient
    ) {
    gitterMessageSource = Flux.from(
    gitterClient
    .getMessages(null)
    )
    .onBackpressureBuffer()
    .publish(1)
    .autoConnect(0);
    }

    View Slide

  157. .onBP()
    @Autowired
    public GitterService(
    GitterClient gitterClient
    ) {
    gitterMessageSource = Flux.from(
    gitterClient
    .getMessages(null)
    )
    .onBackpressureBuffer()
    .publish(1)
    .autoConnect(0);
    }

    View Slide

  158. .onBP()
    @Autowired
    public GitterService(
    GitterClient gitterClient
    ) {
    gitterMessageSource = Flux.from(
    gitterClient
    .getMessages(null)
    )
    .onBackpressureBuffer()
    .publish(1)
    .autoConnect(0);
    }
    …subscribe();

    View Slide

  159. .onBP()
    @Autowired
    public GitterService(
    GitterClient gitterClient
    ) {
    gitterMessageSource = Flux.from(
    gitterClient
    .getMessages(null)
    )
    .onBackpressureBuffer()
    .publish(1)
    .autoConnect(0);
    }
    …subscribe();
    …subscribe();

    View Slide

  160. Message Service

    View Slide

  161. View Slide

  162. Code Session

    View Slide

  163. @Override
    public Flux latest() {
    return chatClient.stream()
    .transform(MessageMapper
    ::toViewModelUnits
    );
    }
    @Override
    public Flux latest() {
    return chatClient.stream()
    .transform(MessageMapper
    ::toViewModelUnits
    );
    }
    @Override
    public Flux latest() {
    return chatClient.stream()
    .transform(MessageMapper
    ::toViewModelUnits
    );
    }
    .transform()

    View Slide

  164. Statistics Service

    View Slide

  165. View Slide

  166. Code Session

    View Slide

  167. @Autowired
    public DefaultMessageService(
    MessageRepository messageRepository,
    ChatService chatClient,
    MessageBroker messageBroker
    ) {
    this.chatClient = chatClient;
    Flux saved = messageRepository
    .saveAll(chatClient.stream()
    .transform(MessageMapper::toDomainUnits)
    );
    messageBroker.createChannel(
    "statisticChanged",
    saved.materialize()
    );
    }

    View Slide

  168. @Autowired
    public DefaultMessageService(
    MessageRepository messageRepository,
    ChatService chatClient,
    MessageBroker messageBroker
    ) {
    this.chatClient = chatClient;
    Flux saved = messageRepository
    .saveAll(chatClient.stream()
    .transform(MessageMapper::toDomainUnits)
    );
    messageBroker.createChannel(
    "statisticChanged",
    saved.materialize()
    );
    }
    .transform()

    View Slide

  169. @Autowired
    public DefaultMessageService(
    MessageRepository messageRepository,
    ChatService chatClient,
    MessageBroker messageBroker
    ) {
    this.chatClient = chatClient;
    Flux saved = messageRepository
    .saveAll(chatClient.stream()
    .transform(MessageMapper::toDomainUnits)
    );
    messageBroker.createChannel(
    "statisticChanged",
    saved.materialize()
    );
    }
    .transform()

    View Slide

  170. @Autowired
    public DefaultMessageService(
    MessageRepository messageRepository,
    ChatService chatClient,
    MessageBroker messageBroker
    ) {
    this.chatClient = chatClient;
    Flux saved = messageRepository
    .saveAll(chatClient.stream()
    .transform(MessageMapper::toDomainUnits)
    );
    messageBroker.createChannel(
    "statisticChanged",
    saved.materialize()
    );
    }
    Channel
    .materialize()

    View Slide

  171. @Autowired
    public DefaultMessageService(
    MessageRepository messageRepository,
    ChatService chatClient,
    MessageBroker messageBroker
    ) {
    this.chatClient = chatClient;
    Flux saved = messageRepository
    .saveAll(chatClient.stream()
    .transform(MessageMapper::toDomainUnits)
    );
    messageBroker.createChannel(
    "statisticChanged",
    saved.materialize()
    );
    }
    .transform()

    View Slide

  172. @Autowired
    public DefaultMessageService(
    MessageRepository messageRepository,
    ChatService chatClient,
    MessageBroker messageBroker
    ) {
    this.chatClient = chatClient;
    Flux saved = messageRepository
    .saveAll(chatClient.stream()
    .transform(MessageMapper::toDomainUnits)
    );
    messageBroker.createChannel(
    "statisticChanged",
    saved.materialize()
    );
    }
    .transform()

    View Slide

  173. @Autowired
    public DefaultMessageService(
    MessageRepository messageRepository,
    ChatService chatClient,
    MessageBroker messageBroker
    ) {
    this.chatClient = chatClient;
    Flux saved = messageRepository
    .saveAll(chatClient.stream()
    .transform(MessageMapper::toDomainUnits)
    );
    messageBroker.createChannel(
    "statisticChanged",
    saved.materialize()
    );
    }
    .transform()

    View Slide

  174. @Autowired
    public DefaultMessageService(
    MessageRepository messageRepository,
    ChatService chatClient,
    MessageBroker messageBroker
    ) {
    this.chatClient = chatClient;
    Flux saved = messageRepository
    .saveAll(chatClient.stream()
    .transform(MessageMapper::toDomainUnits)
    );
    messageBroker.createChannel(
    "statisticChanged",
    saved.materialize()
    );
    }
    .transform()

    View Slide

  175. @Autowired
    public DefaultMessageService(
    MessageRepository messageRepository,
    ChatService chatClient,
    MessageBroker messageBroker
    ) {
    this.chatClient = chatClient;
    Flux saved = messageRepository
    .saveAll(chatClient.stream()
    .transform(MessageMapper::toDomainUnits)
    );
    messageBroker.createChannel(
    "statisticChanged",
    saved.materialize()
    );
    }
    .materialize()
    .transform()

    View Slide

  176. @Autowired
    public DefaultMessageService(
    MessageRepository messageRepository,
    ChatService chatClient,
    MessageBroker messageBroker
    ) {
    this.chatClient = chatClient;
    Flux saved = messageRepository
    .saveAll(chatClient.stream()
    .transform(MessageMapper::toDomainUnits)
    );
    messageBroker.createChannel(
    "statisticChanged",
    saved.materialize()
    );
    }
    .transform()
    .materialize()

    View Slide

  177. @Autowired
    public DefaultMessageService(
    MessageRepository messageRepository,
    ChatService chatClient,
    MessageBroker messageBroker
    ) {
    this.chatClient = chatClient;
    Flux saved = messageRepository
    .saveAll(chatClient.stream()
    .transform(MessageMapper::toDomainUnits)
    );
    messageBroker.createChannel(
    "statisticChanged",
    saved.materialize()
    );
    }
    Channel
    .materialize()

    View Slide

  178. Channel
    @Autowired
    public DefaultMessageService(
    MessageRepository messageRepository,
    ChatService chatClient,
    MessageBroker messageBroker
    ) {
    this.chatClient = chatClient;
    Flux saved = messageRepository
    .saveAll(chatClient.stream()
    .transform(MessageMapper::toDomainUnits)
    );
    messageBroker.createChannel(
    "statisticChanged",
    saved.materialize()
    );
    }
    .materialize()

    View Slide

  179. @Autowired
    public DefaultMessageService(
    MessageRepository messageRepository,
    ChatService chatClient,
    MessageBroker messageBroker
    ) {
    this.chatClient = chatClient;
    Flux saved = messageRepository
    .saveAll(chatClient.stream()
    .transform(MessageMapper::toDomainUnits)
    );
    messageBroker.createChannel(
    "statisticChanged",
    saved.materialize()
    );
    }
    .materialize()
    Channel

    View Slide

  180. Code Session

    View Slide

  181. this.statisticPublisher = messageBroker
    .channel("statisticChanged")
    .retry(err -> err instanceof USE)
    .dematerialize()
    .flatMap(s -> doGetUserStatistic())
    .mergeWith(Mono
    .defer(this::doGetUserStatistic)
    )
    .cache(1);
    .retry()
    Channel

    View Slide

  182. this.statisticPublisher = messageBroker
    .channel("statisticChanged")
    .retry(err -> err instanceof USE)
    .dematerialize()
    .flatMap(s -> doGetUserStatistic())
    .mergeWith(Mono
    .defer(this::doGetUserStatistic)
    )
    .cache(1);
    .retry()
    Channel

    View Slide

  183. .retry()
    Channel
    this.statisticPublisher = messageBroker
    .channel("statisticChanged")
    .retry(err -> err instanceof USE)
    .dematerialize()
    .flatMap(s -> doGetUserStatistic())
    .mergeWith(Mono
    .defer(this::doGetUserStatistic)
    )
    .cache(1);

    View Slide

  184. this.statisticPublisher = messageBroker
    .channel("statisticChanged")
    .retry(err -> err instanceof USE)
    .dematerialize()
    .flatMap(s -> doGetUserStatistic())
    .mergeWith(Mono
    .defer(this::doGetUserStatistic)
    )
    .cache(1);
    .retry()
    Channel

    View Slide

  185. this.statisticPublisher = messageBroker
    .channel("statisticChanged")
    .retry(err -> err instanceof USE)
    .dematerialize()
    .flatMap(s -> doGetUserStatistic())
    .mergeWith(Mono
    .defer(this::doGetUserStatistic)
    )
    .cache(1);
    .retry()
    Channel

    View Slide

  186. this.statisticPublisher = messageBroker
    .channel("statisticChanged")
    .retry(err -> err instanceof USE)
    .dematerialize()
    .flatMap(s -> doGetUserStatistic())
    .mergeWith(Mono
    .defer(this::doGetUserStatistic)
    )
    .cache(1);
    .retry()
    Channel

    View Slide

  187. this.statisticPublisher = messageBroker
    .channel("statisticChanged")
    .retry(err -> err instanceof USE)
    .dematerialize()
    .flatMap(s -> doGetUserStatistic())
    .mergeWith(Mono
    .defer(this::doGetUserStatistic)
    )
    .cache(1);
    .retry()
    Channel

    View Slide

  188. .retry()
    Channel
    this.statisticPublisher = messageBroker
    .channel("statisticChanged")
    .retry(err -> err instanceof USE)
    .dematerialize()
    .flatMap(s -> doGetUserStatistic())
    .mergeWith(Mono
    .defer(this::doGetUserStatistic)
    )
    .cache(1);

    View Slide

  189. this.statisticPublisher = messageBroker
    .channel("statisticChanged")
    .retry(err -> err instanceof USE)
    .dematerialize()
    .flatMap(s -> doGetUserStatistic())
    .mergeWith(Mono
    .defer(this::doGetUserStatistic)
    )
    .cache(1);
    .retry()
    Channel

    View Slide

  190. this.statisticPublisher = messageBroker
    .channel("statisticChanged")
    .retry(err -> err instanceof USE)
    .dematerialize()
    .flatMap(s -> doGetUserStatistic())
    .mergeWith(Mono
    .defer(this::doGetUserStatistic)
    )
    .cache(1);
    .retry()
    Channel

    View Slide

  191. this.statisticPublisher = messageBroker
    .channel("statisticChanged")
    .retry(err -> err instanceof USE)
    .dematerialize()
    .flatMap(s -> doGetUserStatistic())
    .mergeWith(Mono
    .defer(this::doGetUserStatistic)
    )
    .cache(1);
    .retry()
    Channel

    View Slide

  192. this.statisticPublisher = messageBroker
    .channel("statisticChanged")
    .retry(err -> err instanceof USE)
    .dematerialize()
    .flatMap(s -> doGetUserStatistic())
    .mergeWith(Mono
    .defer(this::doGetUserStatistic)
    )
    .cache(1);
    .retry()
    Channel
    .dematerialize()

    View Slide

  193. this.statisticPublisher = messageBroker
    .channel("statisticChanged")
    .retry(err -> err instanceof USE)
    .dematerialize()
    .flatMap(s -> doGetUserStatistic())
    .mergeWith(Mono
    .defer(this::doGetUserStatistic)
    )
    .cache(1);
    this.statisticPublisher = messageBroker
    .channel("statisticChanged")
    .retry(err -> err instanceof USE)
    .dematerialize()
    .flatMap(s -> doGetUserStatistic())
    .mergeWith(Mono
    .defer(this::doGetUserStatistic)
    )
    .cache(1);
    this.statisticPublisher = messageBroker
    .channel("statisticChanged")
    .retry(err -> err instanceof USE)
    .dematerialize()
    .flatMap(s -> doGetUserStatistic())
    .mergeWith(Mono
    .defer(this::doGetUserStatistic)
    )
    .cache(1);
    this.statisticPublisher = messageBroker
    .channel("statisticChanged")
    .retry(err -> err instanceof USE)
    .dematerialize()
    .flatMap(s -> doGetUserStatistic())
    .mergeWith(Mono
    .defer(this::doGetUserStatistic)
    )
    .cache(1);
    Channel
    .retry()
    .dematerialize()

    View Slide

  194. this.statisticPublisher = messageBroker
    .channel("statisticChanged")
    .retry(err -> err instanceof USE)
    .dematerialize()
    .flatMap(s -> doGetUserStatistic())
    .mergeWith(Mono
    .defer(this::doGetUserStatistic)
    )
    .cache(1);
    .flatMap()
    .cache(1)
    .mergeWith
    .defer(statistic)

    View Slide

  195. this.statisticPublisher = messageBroker
    .channel("statisticChanged")
    .retry(err -> err instanceof USE)
    .dematerialize()
    .flatMap(s -> doGetUserStatistic())
    .mergeWith(Mono
    .defer(this::doGetUserStatistic)
    )
    .cache(1);
    .flatMap()
    .cache(1)
    .mergeWith
    .defer(statistic)
    …subscribe();

    View Slide

  196. this.statisticPublisher = messageBroker
    .channel("statisticChanged")
    .retry(err -> err instanceof USE)
    .dematerialize()
    .flatMap(s -> doGetUserStatistic())
    .mergeWith(Mono
    .defer(this::doGetUserStatistic)
    )
    .cache(1);
    .flatMap()
    .cache(1)
    .mergeWith
    .defer(statistic)
    …subscribe();

    View Slide

  197. this.statisticPublisher = messageBroker
    .channel("statisticChanged")
    .retry(err -> err instanceof USE)
    .dematerialize()
    .flatMap(s -> doGetUserStatistic())
    .mergeWith(Mono
    .defer(this::doGetUserStatistic)
    )
    .cache(1);
    .flatMap()
    .cache(1)
    …subscribe();
    this.statisticPublisher = messageBroker
    .channel("statisticChanged")
    .retry(err -> err instanceof USE)
    .dematerialize()
    .flatMap(s -> doGetUserStatistic())
    .mergeWith(Mono
    .defer(this::doGetUserStatistic)
    )
    .cache(1);
    .mergeWith
    .defer(statistic)

    View Slide

  198. this.statisticPublisher = messageBroker
    .channel("statisticChanged")
    .retry(err -> err instanceof USE)
    .dematerialize()
    .flatMap(s -> doGetUserStatistic())
    .mergeWith(Mono
    .defer(this::doGetUserStatistic)
    )
    .cache(1);
    .flatMap()
    .cache(1)
    …subscribe();

    View Slide

  199. this.statisticPublisher = messageBroker
    .channel("statisticChanged")
    .retry(err -> err instanceof USE)
    .dematerialize()
    .flatMap(s -> doGetUserStatistic())
    .mergeWith(Mono
    .defer(this::doGetUserStatistic)
    )
    .cache(1);
    .flatMap()
    .cache(1)
    …subscribe();

    View Slide

  200. this.statisticPublisher = messageBroker
    .channel("statisticChanged")
    .retry(err -> err instanceof USE)
    .dematerialize()
    .flatMap(s -> doGetUserStatistic())
    .mergeWith(Mono
    .defer(this::doGetUserStatistic)
    )
    .cache(1);
    .flatMap()
    .cache(1)
    …subscribe();

    View Slide

  201. this.statisticPublisher = messageBroker
    .channel("statisticChanged")
    .retry(err -> err instanceof USE)
    .dematerialize()
    .flatMap(s -> doGetUserStatistic())
    .mergeWith(Mono
    .defer(this::doGetUserStatistic)
    )
    .cache(1);
    …subscribe();
    .flatMap()
    .cache(1)

    View Slide

  202. this.statisticPublisher = messageBroker
    .channel("statisticChanged")
    .retry(err -> err instanceof USE)
    .dematerialize()
    .flatMap(s -> doGetUserStatistic())
    .mergeWith(Mono
    .defer(this::doGetUserStatistic)
    )
    .cache(1);
    this.statisticPublisher = messageBroker
    .channel("statisticChanged")
    .retry(err -> err instanceof USE)
    .dematerialize()
    .flatMap(s -> doGetUserStatistic())
    .mergeWith(Mono
    .defer(this::doGetUserStatistic)
    )
    .cache(1);
    this.statisticPublisher = messageBroker
    .channel("statisticChanged")
    .retry(err -> err instanceof USE)
    .dematerialize()
    .flatMap(s -> doGetUserStatistic())
    .mergeWith(Mono
    .defer(this::doGetUserStatistic)
    )
    .cache(1);
    Channel
    .retry()
    .dematerialize()

    View Slide

  203. this.statisticPublisher = messageBroker
    .channel("statisticChanged")
    .retry(err -> err instanceof USE)
    .dematerialize()
    .flatMap(s -> doGetUserStatistic())
    .mergeWith(Mono
    .defer(this::doGetUserStatistic)
    )
    .cache(1);
    .retry()
    .dematerialize()
    .flatMap()

    View Slide

  204. .retry()
    .dematerialize()
    .flatMap()
    this.statisticPublisher = messageBroker
    .channel("statisticChanged")
    .retry(err -> err instanceof USE)
    .dematerialize()
    .flatMap(s -> doGetUserStatistic())
    .mergeWith(Mono
    .defer(this::doGetUserStatistic)
    )
    .cache(1);

    View Slide

  205. .dematerialize()
    .flatMap()
    .cache(1)
    this.statisticPublisher = messageBroker
    .channel("statisticChanged")
    .retry(err -> err instanceof USE)
    .dematerialize()
    .flatMap(s -> doGetUserStatistic())
    .mergeWith(Mono
    .defer(this::doGetUserStatistic)
    )
    .cache(1);

    View Slide

  206. .dematerialize()
    .flatMap()
    .cache(1)
    this.statisticPublisher = messageBroker
    .channel("statisticChanged")
    .retry(err -> err instanceof USE)
    .dematerialize()
    .flatMap(s -> doGetUserStatistic())
    .mergeWith(Mono
    .defer(this::doGetUserStatistic)
    )
    .cache(1);

    View Slide

  207. .flatMap()
    .cache(1)
    this.statisticPublisher = messageBroker
    .channel("statisticChanged")
    .retry(err -> err instanceof USE)
    .dematerialize()
    .flatMap(s -> doGetUserStatistic())
    .mergeWith(Mono
    .defer(this::doGetUserStatistic)
    )
    .cache(1);

    View Slide

  208. .flatMap()
    .cache(1)
    this.statisticPublisher = messageBroker
    .channel("statisticChanged")
    .retry(err -> err instanceof USE)
    .dematerialize()
    .flatMap(s -> doGetUserStatistic())
    .mergeWith(Mono
    .defer(this::doGetUserStatistic)
    )
    .cache(1);

    View Slide

  209. Code Session

    View Slide

  210. private Mono doGetUserStatistic
    Flux topActiveUser =
    userRepository
    .findAllOrderedByActivityDesc(PageRequest.o
    .map(UserMapper::toViewModelUnits);
    Flux topMentionedUser =
    userRepository
    .findAllOrderedByMentionDesc(PageRequest.of
    .map(UserMapper::toViewModelUnits);
    return Mono.fromDirect(
    Flux.zip(
    topActiveUser.defaultIfEmpty(EMPTY_USER),
    topMentionedUser.defaultIfEmpty(EMPTY_USER)
    UsersStatisticVM::new
    )
    );
    }
    .map(…)
    .defaultIfEmpty(…)
    .zip(…)

    View Slide

  211. private Mono doGetUserStatistic
    Flux topActiveUser =
    userRepository
    .findAllOrderedByActivityDesc(PageRequest.o
    .map(UserMapper::toViewModelUnits);
    Flux topMentionedUser =
    userRepository
    .findAllOrderedByMentionDesc(PageRequest.of
    .map(UserMapper::toViewModelUnits);
    return Mono.fromDirect(
    Flux.zip(
    topActiveUser.defaultIfEmpty(EMPTY_USER),
    topMentionedUser.defaultIfEmpty(EMPTY_USER)
    UsersStatisticVM::new
    )
    );
    }
    .map(…)
    .defaultIfEmpty(…)
    .zip(…)

    View Slide

  212. private Mono doGetUserStatistic
    Flux topActiveUser =
    userRepository
    .findAllOrderedByActivityDesc(PageRequest.o
    .map(UserMapper::toViewModelUnits);
    Flux topMentionedUser =
    userRepository
    .findAllOrderedByMentionDesc(PageRequest.of
    .map(UserMapper::toViewModelUnits);
    return Mono.fromDirect(
    Flux.zip(
    topActiveUser.defaultIfEmpty(EMPTY_USER),
    topMentionedUser.defaultIfEmpty(EMPTY_USER)
    UsersStatisticVM::new
    )
    );
    }
    .map(…)
    .defaultIfEmpty(…)
    .zip(…)

    View Slide

  213. private Mono doGetUserStatistic
    Flux topActiveUser =
    userRepository
    .findAllOrderedByActivityDesc(PageRequest.o
    .map(UserMapper::toViewModelUnits);
    Flux topMentionedUser =
    userRepository
    .findAllOrderedByMentionDesc(PageRequest.of
    .map(UserMapper::toViewModelUnits);
    return Mono.fromDirect(
    Flux.zip(
    topActiveUser.defaultIfEmpty(EMPTY_USER),
    topMentionedUser.defaultIfEmpty(EMPTY_USER)
    UsersStatisticVM::new
    )
    );
    }
    .map(…)
    .defaultIfEmpty(…)
    .zip(…)

    View Slide

  214. private Mono doGetUserStatistic
    Flux topActiveUser =
    userRepository
    .findAllOrderedByActivityDesc(PageRequest.o
    .map(UserMapper::toViewModelUnits);
    Flux topMentionedUser =
    userRepository
    .findAllOrderedByMentionDesc(PageRequest.of
    .map(UserMapper::toViewModelUnits);
    return Mono.fromDirect(
    Flux.zip(
    topActiveUser.defaultIfEmpty(EMPTY_USER),
    topMentionedUser.defaultIfEmpty(EMPTY_USER)
    UsersStatisticVM::new
    )
    );
    }
    .map(…)
    .defaultIfEmpty(…)
    .zip(…)

    View Slide

  215. private Mono doGetUserStatistic
    Flux topActiveUser =
    userRepository
    .findAllOrderedByActivityDesc(PageRequest.o
    .map(UserMapper::toViewModelUnits);
    Flux topMentionedUser =
    userRepository
    .findAllOrderedByMentionDesc(PageRequest.of
    .map(UserMapper::toViewModelUnits);
    return Mono.fromDirect(
    Flux.zip(
    topActiveUser.defaultIfEmpty(EMPTY_USER),
    topMentionedUser.defaultIfEmpty(EMPTY_USER)
    UsersStatisticVM::new
    )
    );
    }
    .map(…)
    .defaultIfEmpty(…)
    .zip(…)

    View Slide

  216. private Mono doGetUserStatistic
    Flux topActiveUser =
    userRepository
    .findAllOrderedByActivityDesc(PageRequest.o
    .map(UserMapper::toViewModelUnits);
    Flux topMentionedUser =
    userRepository
    .findAllOrderedByMentionDesc(PageRequest.of
    .map(UserMapper::toViewModelUnits);
    return Mono.fromDirect(
    Flux.zip(
    topActiveUser.defaultIfEmpty(EMPTY_USER),
    topMentionedUser.defaultIfEmpty(EMPTY_USER)
    UsersStatisticVM::new
    )
    );
    }
    .defaultIfEmpty(…)
    .zip(…)
    .fromDirect(…)

    View Slide

  217. private Mono doGetUserStatistic
    Flux topActiveUser =
    userRepository
    .findAllOrderedByActivityDesc(PageRequest.o
    .map(UserMapper::toViewModelUnits);
    Flux topMentionedUser =
    userRepository
    .findAllOrderedByMentionDesc(PageRequest.of
    .map(UserMapper::toViewModelUnits);
    return Mono.fromDirect(
    Flux.zip(
    topActiveUser.defaultIfEmpty(EMPTY_USER),
    topMentionedUser.defaultIfEmpty(EMPTY_USER)
    UsersStatisticVM::new
    )
    );
    }
    .defaultIfEmpty(…)
    .zip(…)
    .fromDirect(…)

    View Slide

  218. private Mono doGetUserStatistic
    Flux topActiveUser =
    userRepository
    .findAllOrderedByActivityDesc(PageRequest.o
    .map(UserMapper::toViewModelUnits);
    Flux topMentionedUser =
    userRepository
    .findAllOrderedByMentionDesc(PageRequest.of
    .map(UserMapper::toViewModelUnits);
    return Mono.fromDirect(
    Flux.zip(
    topActiveUser.defaultIfEmpty(EMPTY_USER),
    topMentionedUser.defaultIfEmpty(EMPTY_USER)
    UsersStatisticVM::new
    )
    );
    }
    .map(…)
    .defaultIfEmpty(…)
    .zip(…)
    null

    View Slide

  219. private Mono doGetUserStatistic
    Flux topActiveUser =
    userRepository
    .findAllOrderedByActivityDesc(PageRequest.o
    .map(UserMapper::toViewModelUnits);
    Flux topMentionedUser =
    userRepository
    .findAllOrderedByMentionDesc(PageRequest.of
    .map(UserMapper::toViewModelUnits);
    return Mono.fromDirect(
    Flux.zip(
    topActiveUser.defaultIfEmpty(EMPTY_USER),
    topMentionedUser.defaultIfEmpty(EMPTY_USER)
    UsersStatisticVM::new
    )
    );
    }
    .map(…)
    .defaultIfEmpty(…)
    .zip(…)
    null

    View Slide

  220. private Mono doGetUserStatistic
    Flux topActiveUser =
    userRepository
    .findAllOrderedByActivityDesc(PageRequest.o
    .map(UserMapper::toViewModelUnits);
    Flux topMentionedUser =
    userRepository
    .findAllOrderedByMentionDesc(PageRequest.of
    .map(UserMapper::toViewModelUnits);
    return Mono.fromDirect(
    Flux.zip(
    topActiveUser.defaultIfEmpty(EMPTY_USER),
    topMentionedUser.defaultIfEmpty(EMPTY_USER)
    UsersStatisticVM::new
    )
    );
    }
    .map(…)
    .defaultIfEmpty(…)
    .zip(…)
    null

    View Slide

  221. private Mono doGetUserStatistic
    Flux topActiveUser =
    userRepository
    .findAllOrderedByActivityDesc(PageRequest.o
    .map(UserMapper::toViewModelUnits);
    Flux topMentionedUser =
    userRepository
    .findAllOrderedByMentionDesc(PageRequest.of
    .map(UserMapper::toViewModelUnits);
    return Mono.fromDirect(
    Flux.zip(
    topActiveUser.defaultIfEmpty(EMPTY_USER),
    topMentionedUser.defaultIfEmpty(EMPTY_USER)
    UsersStatisticVM::new
    )
    );
    }
    .map(…)
    .defaultIfEmpty(…)
    .zip(…)
    null
    null

    View Slide

  222. private Mono doGetUserStatistic
    Flux topActiveUser =
    userRepository
    .findAllOrderedByActivityDesc(PageRequest.o
    .map(UserMapper::toViewModelUnits);
    Flux topMentionedUser =
    userRepository
    .findAllOrderedByMentionDesc(PageRequest.of
    .map(UserMapper::toViewModelUnits);
    return Mono.fromDirect(
    Flux.zip(
    topActiveUser.defaultIfEmpty(EMPTY_USER),
    topMentionedUser.defaultIfEmpty(EMPTY_USER)
    UsersStatisticVM::new
    )
    );
    }
    .map(…)
    .defaultIfEmpty(…)
    .zip(…)
    null

    View Slide

  223. private Mono doGetUserStatistic
    Flux topActiveUser =
    userRepository
    .findAllOrderedByActivityDesc(PageRequest.o
    .map(UserMapper::toViewModelUnits);
    Flux topMentionedUser =
    userRepository
    .findAllOrderedByMentionDesc(PageRequest.of
    .map(UserMapper::toViewModelUnits);
    return Mono.fromDirect(
    Flux.zip(
    topActiveUser.defaultIfEmpty(EMPTY_USER),
    topMentionedUser.defaultIfEmpty(EMPTY_USER)
    UsersStatisticVM::new
    )
    );
    }
    .map(…)
    .defaultIfEmpty(…)
    .zip(…)
    null

    View Slide

  224. private Mono doGetUserStatistic
    Flux topActiveUser =
    userRepository
    .findAllOrderedByActivityDesc(PageRequest.o
    .map(UserMapper::toViewModelUnits);
    Flux topMentionedUser =
    userRepository
    .findAllOrderedByMentionDesc(PageRequest.of
    .map(UserMapper::toViewModelUnits);
    return Mono.fromDirect(
    Flux.zip(
    topActiveUser.defaultIfEmpty(EMPTY_USER),
    topMentionedUser.defaultIfEmpty(EMPTY_USER)
    UsersStatisticVM::new
    )
    );
    }
    .map(…)
    .defaultIfEmpty(…)
    .zip(…)
    null

    View Slide

  225. private Mono doGetUserStatistic
    Flux topActiveUser =
    userRepository
    .findAllOrderedByActivityDesc(PageRequest.o
    .map(UserMapper::toViewModelUnits);
    Flux topMentionedUser =
    userRepository
    .findAllOrderedByMentionDesc(PageRequest.of
    .map(UserMapper::toViewModelUnits);
    return Mono.fromDirect(
    Flux.zip(
    topActiveUser.defaultIfEmpty(EMPTY_USER),
    topMentionedUser.defaultIfEmpty(EMPTY_USER)
    UsersStatisticVM::new
    )
    );
    }
    .map(…)
    .defaultIfEmpty(…)
    .zip(…)

    View Slide

  226. private Mono doGetUserStatistic
    Flux topActiveUser =
    userRepository
    .findAllOrderedByActivityDesc(PageRequest.o
    .map(UserMapper::toViewModelUnits);
    Flux topMentionedUser =
    userRepository
    .findAllOrderedByMentionDesc(PageRequest.of
    .map(UserMapper::toViewModelUnits);
    return Mono.fromDirect(
    Flux.zip(
    topActiveUser.defaultIfEmpty(EMPTY_USER),
    topMentionedUser.defaultIfEmpty(EMPTY_USER)
    UsersStatisticVM::new
    )
    );
    }
    .defaultIfEmpty(…)
    .zip(…)
    .fromDirect(…)

    View Slide

  227. private Mono doGetUserStatistic
    Flux topActiveUser =
    userRepository
    .findAllOrderedByActivityDesc(PageRequest.o
    .map(UserMapper::toViewModelUnits);
    Flux topMentionedUser =
    userRepository
    .findAllOrderedByMentionDesc(PageRequest.of
    .map(UserMapper::toViewModelUnits);
    return Mono.fromDirect(
    Flux.zip(
    topActiveUser.defaultIfEmpty(EMPTY_USER),
    topMentionedUser.defaultIfEmpty(EMPTY_USER)
    UsersStatisticVM::new
    )
    );
    }
    .defaultIfEmpty(…)
    .zip(…)
    .fromDirect(…)

    View Slide

  228. private Mono doGetUserStatistic
    Flux topActiveUser =
    userRepository
    .findAllOrderedByActivityDesc(PageRequest.o
    .map(UserMapper::toViewModelUnits);
    Flux topMentionedUser =
    userRepository
    .findAllOrderedByMentionDesc(PageRequest.of
    .map(UserMapper::toViewModelUnits);
    return Mono.fromDirect(
    Flux.zip(
    topActiveUser.defaultIfEmpty(EMPTY_USER),
    topMentionedUser.defaultIfEmpty(EMPTY_USER)
    UsersStatisticVM::new
    )
    );
    }
    private Mono doGetUserStatistic
    Flux topActiveUser =
    userRepository
    .findAllOrderedByActivityDesc(PageRequest.o
    .map(UserMapper::toViewModelUnits);
    Flux topMentionedUser =
    userRepository
    .findAllOrderedByMentionDesc(PageRequest.of
    .map(UserMapper::toViewModelUnits);
    return Mono.fromDirect(
    Flux.zip(
    topActiveUser.defaultIfEmpty(EMPTY_USER),
    topMentionedUser.defaultIfEmpty(EMPTY_USER)
    UsersStatisticVM::new
    )
    );
    }
    .defaultIfEmpty(…)
    .zip(…)
    .fromDirect(…)

    View Slide

  229. private Mono doGetUserStatistic
    Flux topActiveUser =
    userRepository
    .findAllOrderedByActivityDesc(PageRequest.o
    .map(UserMapper::toViewModelUnits);
    Flux topMentionedUser =
    userRepository
    .findAllOrderedByMentionDesc(PageRequest.of
    .map(UserMapper::toViewModelUnits);
    return Mono.fromDirect(
    Flux.zip(
    topActiveUser.defaultIfEmpty(EMPTY_USER),
    topMentionedUser.defaultIfEmpty(EMPTY_USER)
    UsersStatisticVM::new
    )
    );
    }
    .defaultIfEmpty(…)
    .zip(…)
    .fromDirect(…)

    View Slide

  230. Endpoints Refactoring

    View Slide

  231. View Slide

  232. Reactive WS
    out of the box

    View Slide

  233. public interface WebSocketSession {
    String getId();
    HandshakeInfo getHandshakeInfo();
    DataBufferFactory bufferFactory();
    Flux receive();
    Mono send(Publisher messages);
    default Mono close() {
    return close(CloseStatus.NORMAL);
    }
    Mono close(CloseStatus status);
    WebSocketMessage textMessage(String payload);
    WebSocketMessage binaryMessage(
    Function payloadFactory);
    WebSocketMessage pingMessage(
    Function payloadFactory);
    WebSocketMessage pongMessage(
    Function payloadFactory);
    }

    View Slide

  234. public interface WebSocketSession {
    String getId();
    HandshakeInfo getHandshakeInfo();
    DataBufferFactory bufferFactory();
    Flux receive();
    Mono send(Publisher messages);
    default Mono close() {
    return close(CloseStatus.NORMAL);
    }
    Mono close(CloseStatus status);
    WebSocketMessage textMessage(String payload);
    WebSocketMessage binaryMessage(
    Function payloadFactory);
    WebSocketMessage pingMessage(
    Function payloadFactory);
    WebSocketMessage pongMessage(
    Function payloadFactory);
    }

    View Slide

  235. public interface WebSocketSession {
    String getId();
    HandshakeInfo getHandshakeInfo();
    DataBufferFactory bufferFactory();
    Flux receive();
    Mono send(Publisher messages);
    default Mono close() {
    return close(CloseStatus.NORMAL);
    }
    Mono close(CloseStatus status);
    WebSocketMessage textMessage(String payload);
    WebSocketMessage binaryMessage(
    Function payloadFactory);
    WebSocketMessage pingMessage(
    Function payloadFactory);
    WebSocketMessage pongMessage(
    Function payloadFactory);
    }

    View Slide

  236. Code session

    View Slide

  237. @Override
    public Publisher> handle(
    Publisher in
    ) {
    return Flux.merge(
    messageService.latest(),
    statisticService
    .usersStatisticStream()
    );
    }
    network
    .merge()

    View Slide

  238. @Override
    public Publisher> handle(
    Publisher in
    ) {
    return Flux.merge(
    messageService.latest(),
    statisticService
    .usersStatisticStream()
    );
    }
    network
    .merge()

    View Slide

  239. @Override
    public Publisher> handle(
    Publisher in
    ) {
    return Flux.merge(
    messageService.latest(),
    statisticService
    .usersStatisticStream()
    );
    }
    network
    .merge()

    View Slide

  240. @Override
    public Publisher> handle(
    Publisher in
    ) {
    return Flux.merge(
    messageService.latest(),
    statisticService
    .usersStatisticStream()
    );
    }
    network
    .merge()

    View Slide

  241. .merge()
    @Override
    public Publisher> handle(
    Publisher in
    ) {
    return Flux.merge(
    messageService.latest(),
    statisticService
    .usersStatisticStream()
    );
    }
    network

    View Slide

  242. network
    .merge()
    @Override
    public Publisher> handle(
    Publisher in
    ) {
    return Flux.merge(
    messageService.latest(),
    statisticService
    .usersStatisticStream()
    );
    }

    View Slide

  243. network
    .merge()
    @Override
    public Publisher> handle(
    Publisher in
    ) {
    return Flux.merge(
    messageService.latest(),
    statisticService
    .usersStatisticStream()
    );
    }

    View Slide

  244. @Override
    public Publisher> handle(
    Publisher in
    ) {
    return Flux.merge(
    messageService.latest(),
    statisticService
    .usersStatisticStream()
    );
    }
    network
    .merge()

    View Slide

  245. Pipeline Testing

    View Slide

  246. StepVerifier

    View Slide

  247. StepVerifier
    .create(myPublisher)
    .expectNext(“x”, “y”)
    .expectComplete()
    .verify();

    View Slide

  248. StepVerifier
    .create(myPublisher)
    .expectNext(“x”, “y”)
    .expectComplete()
    .verify();

    View Slide

  249. StepVerifier
    .create(myPublisher)
    .expectNext(“x”, “y”)
    .expectComplete()
    .verify();

    View Slide

  250. StepVerifier
    .create(myPublisher)
    .expectNext(“x”, “y”)
    .expectComplete()
    .verify();

    View Slide

  251. StepVerifier
    .create(myPublisher)
    .expectNext(“x”, “y”)
    .expectComplete()
    .verify();

    View Slide

  252. Code session

    View Slide

  253. Demo

    View Slide

  254. View Slide

  255. Checkpoints
    • Message Driven
    • Resilient
    • Responsive
    • Elastic

    View Slide

  256. SUMMARY

    View Slide

  257. Q&A

    View Slide

  258. Resources
    1. Why Reactive? [http://www.oreilly.com/programming/
    free/why-reactive.csp]
    2. Operator Fusion Part 1 [http://akarnokd.blogspot.com/
    2016/03/operator-fusion-part-1.html]
    3. Reactive Streams Specification [http://www.reactive-
    streams.org/]
    4. Learn Project Reactor [http://projectreactor.io/learn]

    View Slide

  259. Thank you!

    View Slide