Slide 1

Slide 1 text

Funktionale Architektur mit Kotlin Michael Sperber & Benedikt Stemmildt Created: 2022-07-05 Tue 13:25 1

Slide 2

Slide 2 text

Funktionale Architektur mit Kotlin Mike Sperber Active Group GmbH @sperbsen Benedikt Stemmildt BLUME2000 @slashBene 2 . 1

Slide 3

Slide 3 text

Funktionale Programmierung 3 . 1

Slide 4

Slide 4 text

BLUME2000 2020 - Schulung "Funktionale Programmierung" 4 . 1

Slide 5

Slide 5 text

BLUME2000 2020 - Realität 5 . 1

Slide 6

Slide 6 text

Functional Programming in Kotlin? Lists / Option Validation Typystem 6 . 1

Slide 7

Slide 7 text

Hexagonale Architektur Source: Wikimedia Commons, CC-SA 4.0 7 . 1

Slide 8

Slide 8 text

Profiling @SuppressWarnings("ThrowsCount", "LongMethod") fun updateWarenkorb( httpWarenkorbVeraenderung: HttpWarenkorbVeraenderung, sessionIdPayload: String, ): Warenkorb { val start = System.currentTimeMillis() val produkt = produktRepository.holeProduktViaProduktNummer(Prod .... val getProduktEnd = System.currentTimeMillis() logger.info { "get Produkt from DB took ${getProduktEnd - start if (produkt == null) { ... } ... val warenkorbVeraenderungTransformationEnd = System.currentTimeM logger.info { "transform into warenkorb-veränderung took ${waren ... val warenkorbVeraenderungValidationEnd = System.currentTimeMilli logger.info { "warenkorb-veraenderung validation took ${warenkor ... 8 . 1

Slide 9

Slide 9 text

Monaden https://funktionale- programmierung.de/2013/04/18/haskell-monaden.html 9 . 1

Slide 11

Slide 11 text

Monaden benutzen CreateProduct(product1, { CreateProduct(product2, { FindAllProducts({ products -> ... })})}) 11 . 1

Slide 12

Slide 12 text

Kotlin ist nicht Java CreateProduct(product1) { CreateProduct(product2) { FindAllProducts { products -> ... }}} 12 . 1

Slide 13

Slide 13 text

Programmbausteine sealed interface ProductM { companion object { fun findAllProducts(): ProductM> = FindAllProducts(::Pure) fun findProductById(id: ProductId): ProductM> = FindProductById(id, ::Pure) fun countProducts(): ProductM = CountProducts(::Pure) fun createProduct(product: Product): ProductM = CreateProduct(product, ::Pure) fun pure(result: A) = Pure(result) } fun bind(next: (A) -> ProductM): ProductM } 13 . 1

Slide 14

Slide 14 text

Programmbausteine zusammensetzen val c1 = createProduct(product1) val c2 = createProduct(product2) c1.bind { c2.bind { findAllProducts.bind { products -> ... }}} 14 . 1

Slide 15

Slide 15 text

Vorher CreateProduct(product1) { CreateProduct(product2) { FindAllProducts { products -> ... }}} 15 . 1

Slide 16

Slide 16 text

Coroutinen und Continuations sealed interface ProductM { suspend fun susp(): A = suspendCoroutine { cocont: Continuation -> val element = cocont.context[ProductCE]!! as ProductCE element.productM = some( bind { result -> cocont.resume(result) element.productM.get() } ) } } 16 . 1

Slide 18

Slide 18 text

Coroutine productM { createProduct(product1).susp() createProduct(product2).susp() val products = findAllProducts().susp() ... } 18 . 1

Slide 19

Slide 19 text

DSL class ProductMCoroutineDsl { suspend fun findAllProducts() = ProductM.findAllProducts().susp() suspend fun findProductById(id: ProductId) = ProductM.findProductById(id).susp() suspend fun countProducts() = ProductM.countProducts().susp() suspend fun createProduct(product: Product) = ProductM.createProduct(product).susp() suspend fun pure(result: A): A = ProductM.pureM(result) } 19 . 1

Slide 20

Slide 20 text

DSL productM { createProduct(product1) createProduct(product2) val products = findAllProducts() ... } 20 . 1

Slide 21

Slide 21 text

Was ist mit dem Profiling? 21 . 1

Slide 24

Slide 24 text

Was ist mit dem Profiling? data class ProfilingRecord(val opSummary: String, val millis: Long) class ProfilingRecorder(var records: MutableList) var then: Long = -1 lateinit var summary: String fun opStarted(summary: String) { val now = System.currentTimeMillis() if (then != -1L) records.add(ProfilingRecord(this.summary, now - then)) this.summary = summary } } 24 . 1

Slide 25

Slide 25 text

Slide 26

Slide 26 text

Separation of Concerns class ProfilingProductM(val db: MutableMap, val recorder: ProfilingRecorder) : InMemoryProductM(db) { override suspend fun run(productM: ProductM): A { recorder.opStarted(productM.summary()) return super.run(productM) } } ☹️ InMemoryProductM muss open sein ☹️ Was ist mit dem tailrec? 26 . 1

Slide 28

Slide 28 text

Spring @SpringBootApplication class ExampleApplicationOne { @Bean fun outRunner(mongo: ReactiveFluentMongoOperations, @Value("\${spring.kafka.bootstrap-servers}") kafkaBootstrapServers: String) : UnsafeProductMRunner { val impl = KafkaProducerProductMDecorator( bootstrapAddress = kafkaBootstrapServers, delegate = KafkaConsumerProductMDecorator( bootstrapAddress = kafkaBootstrapServers, delegate = MongoProductMDecorator(mongo = mongo) ) ) return ImplementationProductMRunner(impl) } ... } 28 . 1

Slide 29

Slide 29 text

Zusammenfassung FP kann OO/hexagonale Architektur verbessern Kotlin + FP = ❤️ Monaden FTW Effekt-Kombination mit Decorator-Pattern funktionale Sprachen + FP = ❤️ ❤️ ❤️ "proper tail calls" fehlen immer noch auf der JVM https://gitlab.com/BeneStem/verticalization-example-service- one 29 . 1

Slide 30

Slide 30 text

iSAQB-Community-Treffen, Stuttgart 25.7.2022, 18:00, Kulturkiosk https://www.meetup.com/isaqb-community/events/286631012/ 30 . 1