Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Reactive for the rest of us

86 views

Published on

Reactive is being supported in more and more widely adopted frameworks and languages. We are expecting to see more coding along reactive paradigms, especially in systems of high throughput.

But what does it mean to go reactive with a team of people coming from a mostly imperative world? In what ways does it feel different, how do you get in the flow and how do you onboard new team members?

Published in: Software
  • Be the first to comment

  • Be the first to like this

Reactive for the rest of us

  1. 1. Reactive for the rest of us Lisa Junger & Stefan Kürten
  2. 2. Key facts: OTTO in numbers ● 3.2 Billion € revenue ● 90% of revenue generated online, ● Up to 10 orders per second Platform ● Leveraging OTTO brand ● Brokering customer:partner relationships ● Platform services.
  3. 3. The Mission Pricing The only source of truth for any price calculations on the marketplace. Expected requirements: ● high load ● low latency
  4. 4. WebMVC Requests Domain logic One Thread per request
  5. 5. WebFlux Pool of non-blocking threads Requests Domain logic
  6. 6. The verdict Using our innovation token on WebFlux
  7. 7. 2. Use Spring WebFlux as web framework Status Context Decision Consequences Date 2018 - 07 - 03 Accepted Bootstrapping the pricing We chose Spring WebFlux (reactive stack), rather than Spring MVC due to an overall curiosity in it. Monitor whether WebFLux works well for our context - should it get in the way, we may have to consider migrating to Spring MVC
  8. 8. The journey
  9. 9. Quick wins Excitement, Warnings and honeymoon
  10. 10. Observer Pattern SubscriberPublisher
  11. 11. Reactor types FluxMono
  12. 12. @Configuration public class PriceByBasketRouter { @Bean public RouterFunction<ServerResponse> priceByBasketRoutes( PriceByBasketHandler priceByBasketHandler) { return RouterFunctions.route( RequestPredicates.POST("/price") .and(RequestPredicates.accept(MediaType.APPLICATION_JSON)) .and(RequestPredicates.contentType(MediaType.APPLICATION_JSON)), priceByBasketHandler::handle); } }
  13. 13. @Component public class PriceByBasketHandler { <...> public Mono<ServerResponse> handle(ServerRequest request) { return request .bodyToMono(BasketPriceRequest.class) .map(BasketPriceResponse::from) .flatMap(this::okResponse) .onErrorResume(ProductNotFoundException.class, t -> Mono.empty()) .switchIfEmpty(this.unprocessableEntityResponse()) .subscribeOn(Schedulers.elastic()); }
  14. 14. Keep ramping up There will always be change
  15. 15. @RestController public class PriceByBasketController { <...> @PostMapping( path = "/price", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) public Mono<BasketPriceResponse> priceByBasketRequest( @RequestBody BasketPriceRequest basketPriceRequest) { return Mono.just(basketPriceRequest) .flatMap(request -> request.toBasket(productRepository)) .map(BasketPriceResponse::from) .subscribeOn(Schedulers.elastic()); } }
  16. 16. It’s so … functional Simple things sometimes seem so hard
  17. 17. Functional programming Object Orientation Message based mindset Transformation based mindset
  18. 18. shipments .zipWith(retailer, Invoice::createFrom) .log("INVOICE CREATE : INVOICE ::", Level.FINE) .flatMap(invoiceRepository::createOrUpdate) .doOnNext(invoicePublisher::publish) .doOnNext(invoiceCreatedObserver::successfullyCreated) .doOnError(invoiceCreatedObserver::creationError) .subscribe( x -> {}, exception -> log.error("Invoice creation error", exception), () -> log.info("Invoice creation success")); }
  19. 19. Webflux for teh win? Expressiveness trumps concerns
  20. 20. Expressiveness Through the valley of tears https://github.com/MostlyAdequate/mostly-adequate-guide Readability Imperative Declarative Where many developers give up on learning FP Where we feel we are
  21. 21. public Mono<ServerResponse> handle(ServerRequest request) { return request .bodyToMono(ProductMapper.class) .map(ProductMapper::toProduct) .flatMap(product -> sendMessage(product).then(ServerResponse.accepted().build())) .doOnError(e -> log.error("Failed to publish product", e)) .onErrorResume( CodecException.class, e -> ServerResponse.status(HttpStatus.UNPROCESSABLE_ENTITY).build()) .onErrorResume(e -> ServerResponse.status(HttpStatus.SERVICE_UNAVAILABLE).build()) .switchIfEmpty(ServerResponse.badRequest().build()); }
  22. 22. Breaking reactive Ways to make your life harder
  23. 23. Problems that used to be solved ● Swagger Does not work with Router Function approach ● Togglz Web console only available for WebMVC It’s the environment, stupid
  24. 24. Beware! ● block() throws assert ● subscribe on main thread pool ● Debugging More or less subtle pitfalls
  25. 25. Going live And nothing happened
  26. 26. Scaling Rotate 70 percent of the team
  27. 27. Train your functional mindset
  28. 28. Expect the Ecosystem to catch up
  29. 29. Be intentional about learning
  30. 30. Beware of breaking reactive
  31. 31. Team Dragonfish Thank you

×