SlideShare una empresa de Scribd logo
1 de 176
Descargar para leer sin conexión
Consiguiendo súperpoderes
para construir aplicaciones
modernas en la JVM con ZIO
JVM Day Colombia 2021
6 de Noviembre
1/60
Jorge Vásquez
Scala Developer @Scalac
2/60
https://scalac.io/careers/
3/60
¿Qué es ZIO?
4/60
¿Qué es ZIO?
— Librería de código abierto para
Scala
4/60
¿Qué es ZIO?
— Librería de código abierto para
Scala
— Permite construir aplicaciones
reactivas de siguiente
generación en la JVM
4/60
Partes que constituyen ZIO
5/60
Partes que constituyen ZIO
— Core
5/60
Partes que constituyen ZIO
— Core
— STM (So!ware Transactional
Memory)
5/60
Partes que constituyen ZIO
— Core
— STM (So!ware Transactional
Memory)
— Streams
5/60
Partes que constituyen ZIO
— Core
— STM (So!ware Transactional
Memory)
— Streams
— Test
5/60
¿Por qué ZIO?
Porque ZIO nos ayuda a usar
Programación Funcional a nivel
macro, para crear aplicaciones
complejas en el mundo real
6/60
7/60
8/60
¿Por qué ZIO?
ZIO nos da súperpoderes y nos
ayuda a ser más productivos a la
hora de construir aplicaciones
modernas:
9/60
¿Por qué ZIO?
ZIO nos da súperpoderes y nos
ayuda a ser más productivos a la
hora de construir aplicaciones
modernas:
— Resolver problemas con
menos esfuerzo
9/60
¿Por qué ZIO?
ZIO nos da súperpoderes y nos
ayuda a ser más productivos a la
hora de construir aplicaciones
modernas:
— Resolver problemas con
menos esfuerzo
— Menor costo de
mantenimiento
9/60
¿Por qué ZIO?
ZIO nos da súperpoderes y nos
ayuda a ser más productivos a la
hora de construir aplicaciones
modernas:
— Resolver problemas con
menos esfuerzo
— Menor costo de
mantenimiento
— Proporciona mayores garantías
en tiempo de compilación
9/60
ZIO... Porque es Escalable
10/60
11/60
12/60
13/60
ZIO... Porque es Escalable
14/60
15/60
ZIO... Porque es ideal para Programación Asíncrona
// Callback hell
s3Get(key = key,
onError = error => log(error),
onSuccess = value => s3Put(key = key,
value = enrichProfile(value),
onError = error => log(error),
onSuccess = _ => ()
)
)
16/60
ZIO... Porque es ideal para Programación Asíncrona
// Callback hell
s3Get(key = key,
onError = error => log(error),
onSuccess = value => s3Put(key = key,
value = enrichProfile(value),
onError = error => log(error),
onSuccess = _ => ()
)
)
16/60
ZIO... Porque es ideal para Programación Asíncrona
// Callback hell
s3Get(key = key,
onError = error => log(error),
onSuccess = value => s3Put(key = key,
value = enrichProfile(value),
onError = error => log(error),
onSuccess = _ => ()
)
)
16/60
ZIO... Porque es ideal para Programación Asíncrona
// Callback hell
s3Get(key = key,
onError = error => log(error),
onSuccess = value => s3Put(key = key,
value = enrichProfile(value),
onError = error => log(error),
onSuccess = _ => ()
)
)
16/60
ZIO... Porque es ideal para Programación Asíncrona
// Callback hell
s3Get(key = key,
onError = error => log(error),
onSuccess = value => s3Put(key = key,
value = enrichProfile(value),
onError = error => log(error),
onSuccess = _ => ()
)
)
16/60
ZIO... Porque es ideal para Programación Asíncrona
// Callback hell
s3Get(key = key,
onError = error => log(error),
onSuccess = value => s3Put(key = key,
value = enrichProfile(value),
onError = error => log(error),
onSuccess = _ => ()
)
)
16/60
ZIO... Porque es ideal para Programación Asíncrona
// Callback hell
s3Get(key = key,
onError = error => log(error),
onSuccess = value => s3Put(key = key,
value = enrichProfile(value),
onError = error => log(error),
onSuccess = _ => ()
)
)
16/60
ZIO... Porque es ideal para Programación Asíncrona
// Callback hell
s3Get(key = key,
onError = error => log(error),
onSuccess = value => s3Put(key = key,
value = enrichProfile(value),
onError = error => log(error),
onSuccess = _ => ()
)
)
16/60
ZIO... Porque es ideal para Programación Asíncrona
// Callback hell
s3Get(key = key,
onError = error => log(error),
onSuccess = value => s3Put(key = key,
value = enrichProfile(value),
onError = error => log(error),
onSuccess = _ => ()
)
)
16/60
17/60
ZIO... Porque es ideal para Programación Asíncrona
// ¡Adiós callbacks!
// ZIO es 100% asíncrono, pero con bloqueo semántico
val enrich =
(for {
value <- s3Get(key)
_ <- s3Put(key, enrichProfile(value))
} yield ()).catchAll(error => log(error))
18/60
ZIO... Porque es ideal para Programación Asíncrona
// ¡Adiós callbacks!
// ZIO es 100% asíncrono, pero con bloqueo semántico
val enrich =
(for {
value <- s3Get(key)
_ <- s3Put(key, enrichProfile(value))
} yield ()).catchAll(error => log(error))
18/60
ZIO... Porque es ideal para Programación Asíncrona
// ¡Adiós callbacks!
// ZIO es 100% asíncrono, pero con bloqueo semántico
val enrich =
(for {
value <- s3Get(key)
_ <- s3Put(key, enrichProfile(value))
} yield ()).catchAll(error => log(error))
18/60
ZIO... Porque es ideal para Programación Asíncrona
// ¡Adiós callbacks!
// ZIO es 100% asíncrono, pero con bloqueo semántico
val enrich =
(for {
value <- s3Get(key)
_ <- s3Put(key, enrichProfile(value))
} yield ()).catchAll(error => log(error))
18/60
ZIO... Porque es ideal para Programación Asíncrona
// ¡Adiós callbacks!
// ZIO es 100% asíncrono, pero con bloqueo semántico
val enrich =
(for {
value <- s3Get(key)
_ <- s3Put(key, enrichProfile(value))
} yield ()).catchAll(error => log(error))
18/60
ZIO... Porque es ideal para Programación Asíncrona
// ¡Adiós callbacks!
// ZIO es 100% asíncrono, pero con bloqueo semántico
val enrich =
(for {
value <- s3Get(key)
_ <- s3Put(key, enrichProfile(value))
} yield ()).catchAll(error => log(error))
18/60
ZIO... Porque permite maximizar el Parelelismo en una
aplicación
// Procesa cada URL de forma secuencial
ZIO.foreach(urls) { url =>
for {
data <- load(url)
json <- parseToJson(data)
transformed <- transform(json)
} yield transformed
}
19/60
ZIO... Porque permite maximizar el Parelelismo en una
aplicación
// Procesa cada URL de forma secuencial
ZIO.foreach(urls) { url =>
for {
data <- load(url)
json <- parseToJson(data)
transformed <- transform(json)
} yield transformed
}
19/60
ZIO... Porque permite maximizar el Parelelismo en una
aplicación
// Procesa cada URL de forma secuencial
ZIO.foreach(urls) { url =>
for {
data <- load(url)
json <- parseToJson(data)
transformed <- transform(json)
} yield transformed
}
19/60
ZIO... Porque permite maximizar el Parelelismo en una
aplicación
// ¡Paralelizar con control preciso es trivial!
ZIO.foreachParN(20)(urls) { url =>
for {
data <- load(url)
json <- parseToJson(data)
transformed <- transform(json)
} yield transformed
}
20/60
ZIO... Porque permite maximizar el Parelelismo en una
aplicación
// ¡Paralelizar con control preciso es trivial!
ZIO.foreachParN(20)(urls) { url =>
for {
data <- load(url)
json <- parseToJson(data)
transformed <- transform(json)
} yield transformed
}
20/60
ZIO... Porque permite maximizar el Parelelismo en una
aplicación
// ¡Paralelizar con control preciso es trivial!
ZIO.foreachParN(20)(urls) { url =>
for {
data <- load(url)
json <- parseToJson(data)
transformed <- transform(json)
} yield transformed
}
20/60
ZIO... Porque permite maximizar el Parelelismo en una
aplicación
21/60
ZIO... Porque permite maximizar el Parelelismo en una
aplicación
— Cada operador en ZIO que puede tener una versión
que se ejecuta en paralelo la tiene
21/60
ZIO... Porque permite maximizar el Parelelismo en una
aplicación
— Cada operador en ZIO que puede tener una versión
que se ejecuta en paralelo la tiene
— Estos operadores paralelos permiten controlar los
límites de concurrencia
21/60
ZIO... Por su poder para Programación Concurrente
// Varios workers consumiendo una cola,
// con back-pressuring automático
def startWorkers(n: Int, queue: Queue[Work]) = {
val worker = queue.take.flatMap(doWork).forever
val workers = List.fill(n)(worker)
ZIO.forkAll(workers)
}
22/60
ZIO... Por su poder para Programación Concurrente
// Varios workers consumiendo una cola,
// con back-pressuring automático
def startWorkers(n: Int, queue: Queue[Work]) = {
val worker = queue.take.flatMap(doWork).forever
val workers = List.fill(n)(worker)
ZIO.forkAll(workers)
}
22/60
ZIO... Por su poder para Programación Concurrente
// Varios workers consumiendo una cola,
// con back-pressuring automático
def startWorkers(n: Int, queue: Queue[Work]) = {
val worker = queue.take.flatMap(doWork).forever
val workers = List.fill(n)(worker)
ZIO.forkAll(workers)
}
22/60
ZIO... Por su poder para Programación Concurrente
// Varios workers consumiendo una cola,
// con back-pressuring automático
def startWorkers(n: Int, queue: Queue[Work]) = {
val worker = queue.take.flatMap(doWork).forever
val workers = List.fill(n)(worker)
ZIO.forkAll(workers)
}
22/60
ZIO... Por su poder para Programación Concurrente
// Varios workers consumiendo una cola,
// con back-pressuring automático
def startWorkers(n: Int, queue: Queue[Work]) = {
val worker = queue.take.flatMap(doWork).forever
val workers = List.fill(n)(worker)
ZIO.forkAll(workers)
}
22/60
ZIO... Por su poder para Programación Concurrente
// Varios workers consumiendo una cola,
// con back-pressuring automático
def startWorkers(n: Int, queue: Queue[Work]) = {
val worker = queue.take.flatMap(doWork).forever
val workers = List.fill(n)(worker)
ZIO.forkAll(workers)
}
22/60
ZIO... Por su poder para Programación Concurrente
// Hacer commit de transacciones condicionales sin Locks o
// Condition Variables, sin condiciones de carrera ni deadlocks:
def acquireConnection(available: TRef[List[Connection]], used: TRef[List[Connection]]) =
STM.atomically {
for {
// Si no se encuentra una conexión disponible, se hace un rollback de la transacción
// y se espera hasta que haya una disponible para reintentar
connection <- available.get.collect {
case head :: _ => head
}
_ <- available.update(_.drop(1))
_ <- used.update(connection :: _)
} yield connection
}
23/60
ZIO... Por su poder para Programación Concurrente
// Hacer commit de transacciones condicionales sin Locks o
// Condition Variables, sin condiciones de carrera ni deadlocks:
def acquireConnection(available: TRef[List[Connection]], used: TRef[List[Connection]]) =
STM.atomically {
for {
// Si no se encuentra una conexión disponible, se hace un rollback de la transacción
// y se espera hasta que haya una disponible para reintentar
connection <- available.get.collect {
case head :: _ => head
}
_ <- available.update(_.drop(1))
_ <- used.update(connection :: _)
} yield connection
}
23/60
ZIO... Por su poder para Programación Concurrente
// Hacer commit de transacciones condicionales sin Locks o
// Condition Variables, sin condiciones de carrera ni deadlocks:
def acquireConnection(available: TRef[List[Connection]], used: TRef[List[Connection]]) =
STM.atomically {
for {
// Si no se encuentra una conexión disponible, se hace un rollback de la transacción
// y se espera hasta que haya una disponible para reintentar
connection <- available.get.collect {
case head :: _ => head
}
_ <- available.update(_.drop(1))
_ <- used.update(connection :: _)
} yield connection
}
23/60
ZIO... Por su poder para Programación Concurrente
// Hacer commit de transacciones condicionales sin Locks o
// Condition Variables, sin condiciones de carrera ni deadlocks:
def acquireConnection(available: TRef[List[Connection]], used: TRef[List[Connection]]) =
STM.atomically {
for {
// Si no se encuentra una conexión disponible, se hace un rollback de la transacción
// y se espera hasta que haya una disponible para reintentar
connection <- available.get.collect {
case head :: _ => head
}
_ <- available.update(_.drop(1))
_ <- used.update(connection :: _)
} yield connection
}
23/60
ZIO... Por su poder para Programación Concurrente
// Hacer commit de transacciones condicionales sin Locks o
// Condition Variables, sin condiciones de carrera ni deadlocks:
def acquireConnection(available: TRef[List[Connection]], used: TRef[List[Connection]]) =
STM.atomically {
for {
// Si no se encuentra una conexión disponible, se hace un rollback de la transacción
// y se espera hasta que haya una disponible para reintentar
connection <- available.get.collect {
case head :: _ => head
}
_ <- available.update(_.drop(1))
_ <- used.update(connection :: _)
} yield connection
}
23/60
ZIO... Por su poder para Programación Concurrente
// Hacer commit de transacciones condicionales sin Locks o
// Condition Variables, sin condiciones de carrera ni deadlocks:
def acquireConnection(available: TRef[List[Connection]], used: TRef[List[Connection]]) =
STM.atomically {
for {
// Si no se encuentra una conexión disponible, se hace un rollback de la transacción
// y se espera hasta que haya una disponible para reintentar
connection <- available.get.collect {
case head :: _ => head
}
_ <- available.update(_.drop(1))
_ <- used.update(connection :: _)
} yield connection
}
23/60
ZIO... Por su poder para Programación Concurrente
// Hacer commit de transacciones condicionales sin Locks o
// Condition Variables, sin condiciones de carrera ni deadlocks:
def acquireConnection(available: TRef[List[Connection]], used: TRef[List[Connection]]) =
STM.atomically {
for {
// Si no se encuentra una conexión disponible, se hace un rollback de la transacción
// y se espera hasta que haya una disponible para reintentar
connection <- available.get.collect {
case head :: _ => head
}
_ <- available.update(_.drop(1))
_ <- used.update(connection :: _)
} yield connection
}
23/60
ZIO... Por su poder para Programación Concurrente
// Hacer commit de transacciones condicionales sin Locks o
// Condition Variables, sin condiciones de carrera ni deadlocks:
def acquireConnection(available: TRef[List[Connection]], used: TRef[List[Connection]]) =
STM.atomically {
for {
// Si no se encuentra una conexión disponible, se hace un rollback de la transacción
// y se espera hasta que haya una disponible para reintentar
connection <- available.get.collect {
case head :: _ => head
}
_ <- available.update(_.drop(1))
_ <- used.update(connection :: _)
} yield connection
}
23/60
ZIO... Porque permite eliminar leaks de recursos
// Empaquetamos la lógica de adquisición y liberación de un recurso usando Managed,
// eliminando cualquier posibilidad de leaks
val managedFile = Managed.make(open(file))(close(_))
// Modularidad: La lógica de uso de un recurso está separada de la lógica
// de adquisición y liberación del mismo
managedFile.use { resource =>
(for {
data <- read(resource)
_ <- aggregateData(data)
} yield ()).forever
}
24/60
ZIO... Porque permite eliminar leaks de recursos
// Empaquetamos la lógica de adquisición y liberación de un recurso usando Managed,
// eliminando cualquier posibilidad de leaks
val managedFile = Managed.make(open(file))(close(_))
// Modularidad: La lógica de uso de un recurso está separada de la lógica
// de adquisición y liberación del mismo
managedFile.use { resource =>
(for {
data <- read(resource)
_ <- aggregateData(data)
} yield ()).forever
}
24/60
ZIO... Porque permite eliminar leaks de recursos
// Empaquetamos la lógica de adquisición y liberación de un recurso usando Managed,
// eliminando cualquier posibilidad de leaks
val managedFile = Managed.make(open(file))(close(_))
// Modularidad: La lógica de uso de un recurso está separada de la lógica
// de adquisición y liberación del mismo
managedFile.use { resource =>
(for {
data <- read(resource)
_ <- aggregateData(data)
} yield ()).forever
}
24/60
ZIO... Porque permite eliminar leaks de recursos
// Empaquetamos la lógica de adquisición y liberación de un recurso usando Managed,
// eliminando cualquier posibilidad de leaks
val managedFile = Managed.make(open(file))(close(_))
// Modularidad: La lógica de uso de un recurso está separada de la lógica
// de adquisición y liberación del mismo
managedFile.use { resource =>
(for {
data <- read(resource)
_ <- aggregateData(data)
} yield ()).forever
}
24/60
ZIO... Porque permite eliminar leaks de recursos
// Empaquetamos la lógica de adquisición y liberación de un recurso usando Managed,
// eliminando cualquier posibilidad de leaks
val managedFile = Managed.make(open(file))(close(_))
// Modularidad: La lógica de uso de un recurso está separada de la lógica
// de adquisición y liberación del mismo
managedFile.use { resource =>
(for {
data <- read(resource)
_ <- aggregateData(data)
} yield ()).forever
}
24/60
ZIO... Porque permite eliminar leaks de recursos
// Combinar varios ZManaged en uno solo
val managedFiles = ZManaged.foreach(files){ file =>
Managed.make(open(file))(close(_))
}
// Todos los archivos se abren y se cierran al mismo tiempo,
// de forma automática
managedFiles.use { resources =>
ZIO.foreach(resources) { resource =>
(for {
data <- read(resource)
_ <- aggregateData(data)
} yield ()).forever
}
}
25/60
ZIO... Porque permite eliminar leaks de recursos
// Combinar varios ZManaged en uno solo
val managedFiles = ZManaged.foreach(files){ file =>
Managed.make(open(file))(close(_))
}
// Todos los archivos se abren y se cierran al mismo tiempo,
// de forma automática
managedFiles.use { resources =>
ZIO.foreach(resources) { resource =>
(for {
data <- read(resource)
_ <- aggregateData(data)
} yield ()).forever
}
}
25/60
ZIO... Porque permite eliminar leaks de recursos
// Combinar varios ZManaged en uno solo
val managedFiles = ZManaged.foreach(files){ file =>
Managed.make(open(file))(close(_))
}
// Todos los archivos se abren y se cierran al mismo tiempo,
// de forma automática
managedFiles.use { resources =>
ZIO.foreach(resources) { resource =>
(for {
data <- read(resource)
_ <- aggregateData(data)
} yield ()).forever
}
}
25/60
ZIO... Porque permite eliminar leaks de recursos
// Combinar varios ZManaged en uno solo
val managedFiles = ZManaged.foreach(files){ file =>
Managed.make(open(file))(close(_))
}
// Todos los archivos se abren y se cierran al mismo tiempo,
// de forma automática
managedFiles.use { resources =>
ZIO.foreach(resources) { resource =>
(for {
data <- read(resource)
_ <- aggregateData(data)
} yield ()).forever
}
}
25/60
ZIO... Porque permite eliminar leaks de recursos
// Combinar varios ZManaged en uno solo
val managedFiles = ZManaged.foreach(files){ file =>
Managed.make(open(file))(close(_))
}
// Todos los archivos se abren y se cierran al mismo tiempo,
// de forma automática
managedFiles.use { resources =>
ZIO.foreach(resources) { resource =>
(for {
data <- read(resource)
_ <- aggregateData(data)
} yield ()).forever
}
}
25/60
ZIO... Porque es Globalmente Eficiente,
automáticamente
val geoLookup = geoIpService.lookup(ipAddress)
val dbLookup = userRepo.getProfile(userId).map(_.location.toLatLong)
// Hacer que dos efectos compitan en paralelo, cancelando el perdedor
val fastest = geoLookup.race(dbLookup)
26/60
ZIO... Porque es Globalmente Eficiente,
automáticamente
val geoLookup = geoIpService.lookup(ipAddress)
val dbLookup = userRepo.getProfile(userId).map(_.location.toLatLong)
// Hacer que dos efectos compitan en paralelo, cancelando el perdedor
val fastest = geoLookup.race(dbLookup)
26/60
ZIO... Porque es Globalmente Eficiente,
automáticamente
val geoLookup = geoIpService.lookup(ipAddress)
val dbLookup = userRepo.getProfile(userId).map(_.location.toLatLong)
// Hacer que dos efectos compitan en paralelo, cancelando el perdedor
val fastest = geoLookup.race(dbLookup)
26/60
ZIO... Porque es Globalmente Eficiente,
automáticamente
val geoLookup = geoIpService.lookup(ipAddress)
val dbLookup = userRepo.getProfile(userId).map(_.location.toLatLong)
// Hacer que dos efectos compitan en paralelo, cancelando el perdedor
val fastest = geoLookup.race(dbLookup)
26/60
ZIO... Porque es Globalmente Eficiente,
automáticamente
val geoLookup = geoIpService.lookup(ipAddress)
val dbLookup = userRepo.getProfile(userId).map(_.location.toLatLong)
// Hacer que dos efectos corran en paralelo,
// Si uno de ellos falla, el otro es automáticamente interrumpido
val fastest = geoLookup.zipPar(dbLookup)
27/60
ZIO... Porque es Globalmente Eficiente,
automáticamente
val geoLookup = geoIpService.lookup(ipAddress)
val dbLookup = userRepo.getProfile(userId).map(_.location.toLatLong)
// Hacer que dos efectos corran en paralelo,
// Si uno de ellos falla, el otro es automáticamente interrumpido
val fastest = geoLookup.zipPar(dbLookup)
27/60
ZIO... Porque es Globalmente Eficiente,
automáticamente
val geoLookup = geoIpService.lookup(ipAddress)
val dbLookup = userRepo.getProfile(userId).map(_.location.toLatLong)
// Hacer que dos efectos corran en paralelo,
// Si uno de ellos falla, el otro es automáticamente interrumpido
val fastest = geoLookup.zipPar(dbLookup)
27/60
ZIO... Porque es Globalmente Eficiente,
automáticamente
val geoLookup = geoIpService.lookup(ipAddress)
val dbLookup = userRepo.getProfile(userId).map(_.location.toLatLong)
// Hacer que dos efectos corran en paralelo,
// Si uno de ellos falla, el otro es automáticamente interrumpido
val fastest = geoLookup.zipPar(dbLookup)
27/60
ZIO... Porque es Globalmente Eficiente,
automáticamente
val slowDbQuery = userRepo.getProfile(userId)
// Cancelar efectos que se ejecutan lentamente, después de un timeout determinado
slowDbQuery.timeout(60.seconds)
28/60
ZIO... Porque es Globalmente Eficiente,
automáticamente
val slowDbQuery = userRepo.getProfile(userId)
// Cancelar efectos que se ejecutan lentamente, después de un timeout determinado
slowDbQuery.timeout(60.seconds)
28/60
ZIO... Porque es Globalmente Eficiente,
automáticamente
val slowDbQuery = userRepo.getProfile(userId)
// Cancelar efectos que se ejecutan lentamente, después de un timeout determinado
slowDbQuery.timeout(60.seconds)
28/60
ZIO... Porque es Globalmente Eficiente,
automáticamente
val slowDbQuery = userRepo.getProfile(userId)
// Cancelar efectos que se ejecutan lentamente, después de un timeout determinado
slowDbQuery.timeout(60.seconds)
28/60
ZIO... Porque permite operar sobre Streams infinitos de
datos
// ZIO Streams nos permite construir pipelines complejos
// que operen sobre flujos de datos posiblemente infinitos,
// de forma declarativa, concurrente, leak-free y usando
// un espacio constante de memoria.
val wordCount =
ZStream
.fromInputStream(Files.newInputStream(path))
.transduce(ZTransducer.utf8Decode)
.transduce(ZTransducer.splitOn(" "))
.run(ZSink.count)
29/60
ZIO... Porque permite operar sobre Streams infinitos de
datos
// ZIO Streams nos permite construir pipelines complejos
// que operen sobre flujos de datos posiblemente infinitos,
// de forma declarativa, concurrente, leak-free y usando
// un espacio constante de memoria.
val wordCount =
ZStream
.fromInputStream(Files.newInputStream(path))
.transduce(ZTransducer.utf8Decode)
.transduce(ZTransducer.splitOn(" "))
.run(ZSink.count)
29/60
ZIO... Porque permite operar sobre Streams infinitos de
datos
// ZIO Streams nos permite construir pipelines complejos
// que operen sobre flujos de datos posiblemente infinitos,
// de forma declarativa, concurrente, leak-free y usando
// un espacio constante de memoria.
val wordCount =
ZStream
.fromInputStream(Files.newInputStream(path))
.transduce(ZTransducer.utf8Decode)
.transduce(ZTransducer.splitOn(" "))
.run(ZSink.count)
29/60
ZIO... Porque permite operar sobre Streams infinitos de
datos
// ZIO Streams nos permite construir pipelines complejos
// que operen sobre flujos de datos posiblemente infinitos,
// de forma declarativa, concurrente, leak-free y usando
// un espacio constante de memoria.
val wordCount =
ZStream
.fromInputStream(Files.newInputStream(path))
.transduce(ZTransducer.utf8Decode)
.transduce(ZTransducer.splitOn(" "))
.run(ZSink.count)
29/60
ZIO... Porque permite operar sobre Streams infinitos de
datos
// ZIO Streams nos permite construir pipelines complejos
// que operen sobre flujos de datos posiblemente infinitos,
// de forma declarativa, concurrente, leak-free y usando
// un espacio constante de memoria.
val wordCount =
ZStream
.fromInputStream(Files.newInputStream(path))
.transduce(ZTransducer.utf8Decode)
.transduce(ZTransducer.splitOn(" "))
.run(ZSink.count)
29/60
ZIO... Porque permite operar sobre Streams infinitos de
datos
// ZIO Streams nos permite construir pipelines complejos
// que operen sobre flujos de datos posiblemente infinitos,
// de forma declarativa, concurrente, leak-free y usando
// un espacio constante de memoria.
val wordCount =
ZStream
.fromInputStream(Files.newInputStream(path))
.transduce(ZTransducer.utf8Decode)
.transduce(ZTransducer.splitOn(" "))
.run(ZSink.count)
29/60
ZIO... Porque nos permite crear aplicaciones fáciles de
testear
// ZIO permite alta testabilidad a través del ZIO Environment, el cual hace que sea fácil para los desarrolladores
// seguir buenas prácticas de Programación Orientada a Objetos, por ejemplo:
// "Codificar contra interfaces, no implementaciones".
// Además, ZIO nos permite escribir tests que sean rápidos y determinísticos,
// incluso para aplicaciones interactivas, no determinísticas
val program: ZIO[Has[Clock] with Has[Console], IOException, Unit] =
for {
_ <- putStrLn("What is your name?")
name <- getStrLn
_ <- putStrLn(s"I will wait ${name.length} seconds, $name")
_ <- ZIO.sleep(name.length.seconds)
} yield ()
// testServices contiene implementaciones de prueba para Clock y Console
val deterministicResult = program.provideLayer(testServices)
30/60
ZIO... Porque nos permite crear aplicaciones fáciles de
testear
// ZIO permite alta testabilidad a través del ZIO Environment, el cual hace que sea fácil para los desarrolladores
// seguir buenas prácticas de Programación Orientada a Objetos, por ejemplo:
// "Codificar contra interfaces, no implementaciones".
// Además, ZIO nos permite escribir tests que sean rápidos y determinísticos,
// incluso para aplicaciones interactivas, no determinísticas
val program: ZIO[Has[Clock] with Has[Console], IOException, Unit] =
for {
_ <- putStrLn("What is your name?")
name <- getStrLn
_ <- putStrLn(s"I will wait ${name.length} seconds, $name")
_ <- ZIO.sleep(name.length.seconds)
} yield ()
// testServices contiene implementaciones de prueba para Clock y Console
val deterministicResult = program.provideLayer(testServices)
30/60
ZIO... Porque nos permite crear aplicaciones fáciles de
testear
// ZIO permite alta testabilidad a través del ZIO Environment, el cual hace que sea fácil para los desarrolladores
// seguir buenas prácticas de Programación Orientada a Objetos, por ejemplo:
// "Codificar contra interfaces, no implementaciones".
// Además, ZIO nos permite escribir tests que sean rápidos y determinísticos,
// incluso para aplicaciones interactivas, no determinísticas
val program: ZIO[Has[Clock] with Has[Console], IOException, Unit] =
for {
_ <- putStrLn("What is your name?")
name <- getStrLn
_ <- putStrLn(s"I will wait ${name.length} seconds, $name")
_ <- ZIO.sleep(name.length.seconds)
} yield ()
// testServices contiene implementaciones de prueba para Clock y Console
val deterministicResult = program.provideLayer(testServices)
30/60
ZIO... Porque nos permite crear aplicaciones fáciles de
testear
// ZIO permite alta testabilidad a través del ZIO Environment, el cual hace que sea fácil para los desarrolladores
// seguir buenas prácticas de Programación Orientada a Objetos, por ejemplo:
// "Codificar contra interfaces, no implementaciones".
// Además, ZIO nos permite escribir tests que sean rápidos y determinísticos,
// incluso para aplicaciones interactivas, no determinísticas
val program: ZIO[Has[Clock] with Has[Console], IOException, Unit] =
for {
_ <- putStrLn("What is your name?")
name <- getStrLn
_ <- putStrLn(s"I will wait ${name.length} seconds, $name")
_ <- ZIO.sleep(name.length.seconds)
} yield ()
// testServices contiene implementaciones de prueba para Clock y Console
val deterministicResult = program.provideLayer(testServices)
30/60
ZIO... Porque nos permite crear aplicaciones fáciles de
testear
// ZIO permite alta testabilidad a través del ZIO Environment, el cual hace que sea fácil para los desarrolladores
// seguir buenas prácticas de Programación Orientada a Objetos, por ejemplo:
// "Codificar contra interfaces, no implementaciones".
// Además, ZIO nos permite escribir tests que sean rápidos y determinísticos,
// incluso para aplicaciones interactivas, no determinísticas
val program: ZIO[Has[Clock] with Has[Console], IOException, Unit] =
for {
_ <- putStrLn("What is your name?")
name <- getStrLn
_ <- putStrLn(s"I will wait ${name.length} seconds, $name")
_ <- ZIO.sleep(name.length.seconds)
} yield ()
// testServices contiene implementaciones de prueba para Clock y Console
val deterministicResult = program.provideLayer(testServices)
30/60
ZIO... Porque nos permite crear aplicaciones resilientes
// Deja que el compilador te diga qué puede fallar, y por qué
val result: IO[RequestException, Result] = callFlakyApi(request)
// ZIO aprovecha al máximo el poder del compilador de Scala,
// para que guiados por él podamos construir aplicaciones resilientes
// Por ejemplo, ZIO nos permite introducir reintentos que se ejecuten
// de acuerdo a un Schedule específico
val retryPolicy =
(Schedule.exponential(10.millis).whileOutput(_ < 1.second) andThen
Schedule.spaced(60.seconds)
) && Schedule.recurs(100)
val robustResult: IO[RequestException, Result] = result.retry(retryPolicy)
// Deja que el compilador te diga qué NO puede fallar
val infallible: IO[Nothing, Result] = robustResult.catchAll(_ => fallback)
31/60
ZIO... Porque nos permite crear aplicaciones resilientes
// Deja que el compilador te diga qué puede fallar, y por qué
val result: IO[RequestException, Result] = callFlakyApi(request)
// ZIO aprovecha al máximo el poder del compilador de Scala,
// para que guiados por él podamos construir aplicaciones resilientes
// Por ejemplo, ZIO nos permite introducir reintentos que se ejecuten
// de acuerdo a un Schedule específico
val retryPolicy =
(Schedule.exponential(10.millis).whileOutput(_ < 1.second) andThen
Schedule.spaced(60.seconds)
) && Schedule.recurs(100)
val robustResult: IO[RequestException, Result] = result.retry(retryPolicy)
// Deja que el compilador te diga qué NO puede fallar
val infallible: IO[Nothing, Result] = robustResult.catchAll(_ => fallback)
31/60
ZIO... Porque nos permite crear aplicaciones resilientes
// Deja que el compilador te diga qué puede fallar, y por qué
val result: IO[RequestException, Result] = callFlakyApi(request)
// ZIO aprovecha al máximo el poder del compilador de Scala,
// para que guiados por él podamos construir aplicaciones resilientes
// Por ejemplo, ZIO nos permite introducir reintentos que se ejecuten
// de acuerdo a un Schedule específico
val retryPolicy =
(Schedule.exponential(10.millis).whileOutput(_ < 1.second) andThen
Schedule.spaced(60.seconds)
) && Schedule.recurs(100)
val robustResult: IO[RequestException, Result] = result.retry(retryPolicy)
// Deja que el compilador te diga qué NO puede fallar
val infallible: IO[Nothing, Result] = robustResult.catchAll(_ => fallback)
31/60
ZIO... Porque nos permite crear aplicaciones resilientes
// Deja que el compilador te diga qué puede fallar, y por qué
val result: IO[RequestException, Result] = callFlakyApi(request)
// ZIO aprovecha al máximo el poder del compilador de Scala,
// para que guiados por él podamos construir aplicaciones resilientes
// Por ejemplo, ZIO nos permite introducir reintentos que se ejecuten
// de acuerdo a un Schedule específico
val retryPolicy =
(Schedule.exponential(10.millis).whileOutput(_ < 1.second) andThen
Schedule.spaced(60.seconds)
) && Schedule.recurs(100)
val robustResult: IO[RequestException, Result] = result.retry(retryPolicy)
// Deja que el compilador te diga qué NO puede fallar
val infallible: IO[Nothing, Result] = robustResult.catchAll(_ => fallback)
31/60
ZIO... Porque nos permite crear aplicaciones resilientes
// Deja que el compilador te diga qué puede fallar, y por qué
val result: IO[RequestException, Result] = callFlakyApi(request)
// ZIO aprovecha al máximo el poder del compilador de Scala,
// para que guiados por él podamos construir aplicaciones resilientes
// Por ejemplo, ZIO nos permite introducir reintentos que se ejecuten
// de acuerdo a un Schedule específico
val retryPolicy =
(Schedule.exponential(10.millis).whileOutput(_ < 1.second) andThen
Schedule.spaced(60.seconds)
) && Schedule.recurs(100)
val robustResult: IO[RequestException, Result] = result.retry(retryPolicy)
// Deja que el compilador te diga qué NO puede fallar
val infallible: IO[Nothing, Result] = robustResult.catchAll(_ => fallback)
31/60
ZIO... Porque nos permite crear aplicaciones resilientes
// Deja que el compilador te diga qué puede fallar, y por qué
val result: IO[RequestException, Result] = callFlakyApi(request)
// ZIO aprovecha al máximo el poder del compilador de Scala,
// para que guiados por él podamos construir aplicaciones resilientes
// Por ejemplo, ZIO nos permite introducir reintentos que se ejecuten
// de acuerdo a un Schedule específico
val retryPolicy =
(Schedule.exponential(10.millis).whileOutput(_ < 1.second) andThen
Schedule.spaced(60.seconds)
) && Schedule.recurs(100)
val robustResult: IO[RequestException, Result] = result.retry(retryPolicy)
// Deja que el compilador te diga qué NO puede fallar
val infallible: IO[Nothing, Result] = robustResult.catchAll(_ => fallback)
31/60
ZIO... Porque nos permite crear aplicaciones resilientes
// Deja que el compilador te diga qué puede fallar, y por qué
val result: IO[RequestException, Result] = callFlakyApi(request)
// ZIO aprovecha al máximo el poder del compilador de Scala,
// para que guiados por él podamos construir aplicaciones resilientes
// Por ejemplo, ZIO nos permite introducir reintentos que se ejecuten
// de acuerdo a un Schedule específico
val retryPolicy =
(Schedule.exponential(10.millis).whileOutput(_ < 1.second) andThen
Schedule.spaced(60.seconds)
) && Schedule.recurs(100)
val robustResult: IO[RequestException, Result] = result.retry(retryPolicy)
// Deja que el compilador te diga qué NO puede fallar
val infallible: IO[Nothing, Result] = robustResult.catchAll(_ => fallback)
31/60
ZIO... Porque es altamente Composicional
32/60
ZIO... Porque es altamente Composicional
val managedData = Managed.make(open(url))(close(_))
managedData.use { data =>
searchBreadth(data)
}
33/60
34/60
ZIO... Porque es altamente Composicional
ZIO.foreach(urls) { url =>
val managedData = Managed.make(open(url))(close(_))
managedData.use { data =>
searchBreadth(data)
}
}
35/60
ZIO... Porque es altamente Composicional
ZIO.foreach(urls) { url =>
val managedData = Managed.make(open(url))(close(_))
managedData.use { data =>
searchBreadth(data)
}
}
35/60
36/60
ZIO... Porque es altamente Composicional
ZIO.foreachPar(urls) { url =>
val managedData = Managed.make(open(url))(close(_))
managedData.use { data =>
searchBreadth(data)
}
}
37/60
ZIO... Porque es altamente Composicional
ZIO.foreachPar(urls) { url =>
val managedData = Managed.make(open(url))(close(_))
managedData.use { data =>
searchBreadth(data)
}
}
37/60
38/60
ZIO... Porque es altamente Composicional
ZIO.foreachParN(20)(urls) { url =>
val managedData = Managed.make(open(url))(close(_))
managedData.use { data =>
searchBreadth(data)
}
}
39/60
ZIO... Porque es altamente Composicional
ZIO.foreachParN(20)(urls) { url =>
val managedData = Managed.make(open(url))(close(_))
managedData.use { data =>
searchBreadth(data)
}
}
39/60
40/60
ZIO... Porque es altamente Composicional
val policy = Schedule.recurs(100)
ZIO.foreachParN(20)(urls) { url =>
val managedData = Managed.make(open(url))(close(_))
val robustData = managedData.retry(policy)
robustData.use { data =>
searchBreadth(data)
}
}
41/60
ZIO... Porque es altamente Composicional
val policy = Schedule.recurs(100)
ZIO.foreachParN(20)(urls) { url =>
val managedData = Managed.make(open(url))(close(_))
val robustData = managedData.retry(policy)
robustData.use { data =>
searchBreadth(data)
}
}
41/60
ZIO... Porque es altamente Composicional
val policy = Schedule.recurs(100)
ZIO.foreachParN(20)(urls) { url =>
val managedData = Managed.make(open(url))(close(_))
val robustData = managedData.retry(policy)
robustData.use { data =>
searchBreadth(data)
}
}
41/60
42/60
ZIO... Porque es altamente Composicional
val policy = Schedule.recurs(100) && Schedule.exponential(10.millis)
ZIO.foreachParN(20)(urls) { url =>
val managedData = Managed.make(open(url))(close(_))
val robustData = managedData.retry(policy)
robustData.use { data =>
searchBreadth(data)
}
}
43/60
ZIO... Porque es altamente Composicional
val policy = Schedule.recurs(100) && Schedule.exponential(10.millis)
ZIO.foreachParN(20)(urls) { url =>
val managedData = Managed.make(open(url))(close(_))
val robustData = managedData.retry(policy)
robustData.use { data =>
searchBreadth(data)
}
}
43/60
44/60
ZIO... Porque es altamente Composicional
val policy = Schedule.recurs(100) && Schedule.exponential(10.millis)
ZIO.foreachParN(20)(urls) { url =>
val managedData = Managed.make(open(url))(close(_))
val robustData = managedData.retry(policy).timeoutFail(30.seconds)
robustData.use { data =>
searchBreadth(data)
}
}
45/60
ZIO... Porque es altamente Composicional
val policy = Schedule.recurs(100) && Schedule.exponential(10.millis)
ZIO.foreachParN(20)(urls) { url =>
val managedData = Managed.make(open(url))(close(_))
val robustData = managedData.retry(policy).timeoutFail(30.seconds)
robustData.use { data =>
searchBreadth(data)
}
}
45/60
46/60
ZIO... Porque es altamente Composicional
val policy = Schedule.recurs(100) && Schedule.exponential(10.millis)
ZIO.foreachParN(20)(urls) { url =>
val managedData = Managed.make(open(url))(close(_))
val robustData = managedData.retry(policy).timeoutFail(30.seconds)
robustData.use { data =>
searchBreadth(data).race(searchDepth(data))
}
}
47/60
ZIO... Porque es altamente Composicional
val policy = Schedule.recurs(100) && Schedule.exponential(10.millis)
ZIO.foreachParN(20)(urls) { url =>
val managedData = Managed.make(open(url))(close(_))
val robustData = managedData.retry(policy).timeoutFail(30.seconds)
robustData.use { data =>
searchBreadth(data).race(searchDepth(data))
}
}
47/60
48/60
ZIO... Porque es altamente Composicional
val policy = Schedule.recurs(100) && Schedule.exponential(10.millis)
ZIO.foreachParN(20)(urls) { url =>
val managedData = Managed.make(open(url))(close(_))
val robustData = managedData.retry(policy).timeoutFail(30.seconds)
robustData.use { data =>
searchBreadth(data).race(searchDepth(data))
}
}.timeout(10.minutes)
49/60
ZIO... Porque es altamente Composicional
val policy = Schedule.recurs(100) && Schedule.exponential(10.millis)
ZIO.foreachParN(20)(urls) { url =>
val managedData = Managed.make(open(url))(close(_))
val robustData = managedData.retry(policy).timeoutFail(30.seconds)
robustData.use { data =>
searchBreadth(data).race(searchDepth(data))
}
}.timeout(10.minutes)
49/60
ZIO... Porque es altamente Composicional
val policy = Schedule.recurs(100) && Schedule.exponential(10.millis)
ZIO.foreachParN(20)(urls) { url =>
val managedData = Managed.make(open(url))(close(_))
val robustData = managedData.retry(policy).timeoutFail(30.seconds)
robustData.use { data =>
searchBreadth(data).race(searchDepth(data))
}
}.timeout(10.minutes)
49/60
50/60
51/60
52/60
Mejoras que serán introducidas
en ZIO 2.0
53/60
Mejoras que serán introducidas
en ZIO 2.0
— Mejor performance
53/60
Mejoras que serán introducidas
en ZIO 2.0
— Mejor performance
— Mejor Fiber Scheduler
53/60
Mejoras que serán introducidas
en ZIO 2.0
— Mejor performance
— Mejor Fiber Scheduler
— API más ergonómica
53/60
Mejoras que serán introducidas
en ZIO 2.0
54/60
Mejoras que serán introducidas
en ZIO 2.0
— Profiler incorporado
54/60
Mejoras que serán introducidas
en ZIO 2.0
— Profiler incorporado
— Mejor execution tracing, con
prácticamente cero overhead
54/60
Mejoras que serán introducidas
en ZIO 2.0
— Profiler incorporado
— Mejor execution tracing, con
prácticamente cero overhead
— Métricas incorporadas, con
soporte por defecto para:
54/60
Mejoras que serán introducidas
en ZIO 2.0
— Profiler incorporado
— Mejor execution tracing, con
prácticamente cero overhead
— Métricas incorporadas, con
soporte por defecto para:
— Datadog
54/60
Mejoras que serán introducidas
en ZIO 2.0
— Profiler incorporado
— Mejor execution tracing, con
prácticamente cero overhead
— Métricas incorporadas, con
soporte por defecto para:
— Datadog
— StatsD
54/60
Mejoras que serán introducidas
en ZIO 2.0
— Profiler incorporado
— Mejor execution tracing, con
prácticamente cero overhead
— Métricas incorporadas, con
soporte por defecto para:
— Datadog
— StatsD
— Prometheus
54/60
Mejoras que serán introducidas
en ZIO 2.0
— Profiler incorporado
— Mejor execution tracing, con
prácticamente cero overhead
— Métricas incorporadas, con
soporte por defecto para:
— Datadog
— StatsD
— Prometheus
— New Relic
54/60
El Ecosistema de ZIO
55/60
El Ecosistema de ZIO
— ZIO CLI
55/60
El Ecosistema de ZIO
— ZIO CLI
— ZIO Cache
55/60
El Ecosistema de ZIO
— ZIO CLI
— ZIO Cache
— ZIO Json
55/60
El Ecosistema de ZIO
— ZIO CLI
— ZIO Cache
— ZIO Json
— ZIO Http
55/60
El Ecosistema de ZIO
— ZIO CLI
— ZIO Cache
— ZIO Json
— ZIO Http
— Caliban
55/60
El Ecosistema de ZIO
56/60
El Ecosistema de ZIO
— ZIO Microservices
56/60
El Ecosistema de ZIO
— ZIO Microservices
— ZIO gRPC
56/60
El Ecosistema de ZIO
— ZIO Microservices
— ZIO gRPC
— ZIO Flow
56/60
El Ecosistema de ZIO
— ZIO Microservices
— ZIO gRPC
— ZIO Flow
— ZIO Quill
56/60
El Ecosistema de ZIO
— ZIO Microservices
— ZIO gRPC
— ZIO Flow
— ZIO Quill
— ZIO SQL
56/60
El Ecosistema de ZIO
57/60
El Ecosistema de ZIO
— ZIO Query
57/60
El Ecosistema de ZIO
— ZIO Query
— ZIO Kafka
57/60
El Ecosistema de ZIO
— ZIO Query
— ZIO Kafka
— ZIO Pulsar
57/60
El Ecosistema de ZIO
— ZIO Query
— ZIO Kafka
— ZIO Pulsar
— ZIO AWS
57/60
El Ecosistema de ZIO
— ZIO Query
— ZIO Kafka
— ZIO Pulsar
— ZIO AWS
— ZIO Redis
57/60
El Ecosistema de ZIO
— ZIO Query
— ZIO Kafka
— ZIO Pulsar
— ZIO AWS
— ZIO Redis
— Y muchas otras más...
57/60
Dónde encontrar más info
acerca de ZIO
58/60
Dónde encontrar más info
acerca de ZIO
— Página oficial de ZIO
58/60
Dónde encontrar más info
acerca de ZIO
— Página oficial de ZIO
— ZIO en GitHub
58/60
Dónde encontrar más info
acerca de ZIO
— Página oficial de ZIO
— ZIO en GitHub
— Zionomicon
58/60
Dónde encontrar más info
acerca de ZIO
— Página oficial de ZIO
— ZIO en GitHub
— Zionomicon
— "The ZIO of the Future", charla
por John De Goes (autor de
ZIO)
58/60
Dónde encontrar más info
acerca de ZIO
59/60
Dónde encontrar más info
acerca de ZIO
— Introducción a la
Programación con Efectos
Funcionales usando ZIO
59/60
Dónde encontrar más info
acerca de ZIO
— Introducción a la
Programación con Efectos
Funcionales usando ZIO
— Functional Programming 101
with Scala and ZIO
59/60
Dónde encontrar más info
acerca de ZIO
— Introducción a la
Programación con Efectos
Funcionales usando ZIO
— Functional Programming 101
with Scala and ZIO
— Mastering Modularity in ZIO
with ZLayer
59/60
Dónde encontrar más info
acerca de ZIO
— Introducción a la
Programación con Efectos
Funcionales usando ZIO
— Functional Programming 101
with Scala and ZIO
— Mastering Modularity in ZIO
with ZLayer
— How to write a (completely
lock-free) concurrent LRU
Cache with ZIO STM
59/60
¡Gracias!
@jorvasquez2301
jorge-vasquez-2301
jorge.vasquez@scalac.io
60/60

Más contenido relacionado

Similar a Consiguiendo superpoderes para construir aplicaciones modernas en la JVM con ZIO

Jose Selvi - Adaptando exploits para evitar la frustración [RootedSatellite V...
Jose Selvi - Adaptando exploits para evitar la frustración [RootedSatellite V...Jose Selvi - Adaptando exploits para evitar la frustración [RootedSatellite V...
Jose Selvi - Adaptando exploits para evitar la frustración [RootedSatellite V...
RootedCON
 
Asterisk apis agi amiasync
Asterisk apis agi amiasyncAsterisk apis agi amiasync
Asterisk apis agi amiasync
4kconference
 
Cambia la forma de desarrollar tus aplicaciones web con groovy y grails
Cambia la forma de desarrollar tus aplicaciones web con groovy y grailsCambia la forma de desarrollar tus aplicaciones web con groovy y grails
Cambia la forma de desarrollar tus aplicaciones web con groovy y grails
Fátima Casaú Pérez
 
Symfony y Admin Generator
Symfony y Admin GeneratorSymfony y Admin Generator
Symfony y Admin Generator
Javier López
 
Node js Alt.net Hispano
Node js Alt.net HispanoNode js Alt.net Hispano
Node js Alt.net Hispano
hdgarcia
 

Similar a Consiguiendo superpoderes para construir aplicaciones modernas en la JVM con ZIO (20)

Jose Selvi - Adaptando exploits para evitar la frustración [RootedSatellite V...
Jose Selvi - Adaptando exploits para evitar la frustración [RootedSatellite V...Jose Selvi - Adaptando exploits para evitar la frustración [RootedSatellite V...
Jose Selvi - Adaptando exploits para evitar la frustración [RootedSatellite V...
 
Introducción al microframework PHP Silex - Sergio Gómez - Betabeers Córdoba 0...
Introducción al microframework PHP Silex - Sergio Gómez - Betabeers Córdoba 0...Introducción al microframework PHP Silex - Sergio Gómez - Betabeers Córdoba 0...
Introducción al microframework PHP Silex - Sergio Gómez - Betabeers Córdoba 0...
 
Asterisk apis agi amiasync
Asterisk apis agi amiasyncAsterisk apis agi amiasync
Asterisk apis agi amiasync
 
Node Js & 3D Printer
Node Js & 3D PrinterNode Js & 3D Printer
Node Js & 3D Printer
 
Groovy&Grails: Cambia la forma de desarrollar tus aplicaciones web
Groovy&Grails: Cambia la forma de desarrollar tus aplicaciones webGroovy&Grails: Cambia la forma de desarrollar tus aplicaciones web
Groovy&Grails: Cambia la forma de desarrollar tus aplicaciones web
 
Cambia la forma de desarrollar tus aplicaciones web con groovy y grails
Cambia la forma de desarrollar tus aplicaciones web con groovy y grailsCambia la forma de desarrollar tus aplicaciones web con groovy y grails
Cambia la forma de desarrollar tus aplicaciones web con groovy y grails
 
Seguridad so pii_2011
Seguridad so pii_2011Seguridad so pii_2011
Seguridad so pii_2011
 
Codemotion 2017 - Taller de JHipster
Codemotion 2017 - Taller de JHipsterCodemotion 2017 - Taller de JHipster
Codemotion 2017 - Taller de JHipster
 
Reactividad en Angular, React y VueJS
Reactividad en Angular, React y VueJSReactividad en Angular, React y VueJS
Reactividad en Angular, React y VueJS
 
Monitorizando aplicaciones con AspectJ
Monitorizando aplicaciones con AspectJMonitorizando aplicaciones con AspectJ
Monitorizando aplicaciones con AspectJ
 
Symfony: Domesticando las Vistas - decharlas
Symfony: Domesticando las Vistas - decharlasSymfony: Domesticando las Vistas - decharlas
Symfony: Domesticando las Vistas - decharlas
 
Symfony: Domesticando las Vistas
Symfony: Domesticando las VistasSymfony: Domesticando las Vistas
Symfony: Domesticando las Vistas
 
symfony admin generator - decharlas
symfony admin generator - decharlassymfony admin generator - decharlas
symfony admin generator - decharlas
 
Symfony y Admin Generator
Symfony y Admin GeneratorSymfony y Admin Generator
Symfony y Admin Generator
 
Curso arduino basico bitbloq
Curso arduino basico bitbloqCurso arduino basico bitbloq
Curso arduino basico bitbloq
 
Node js Alt.net Hispano
Node js Alt.net HispanoNode js Alt.net Hispano
Node js Alt.net Hispano
 
Jerónimo López | Introducción a GraalVM | Codemotion Madrid 2018
Jerónimo López | Introducción a GraalVM | Codemotion Madrid  2018  Jerónimo López | Introducción a GraalVM | Codemotion Madrid  2018
Jerónimo López | Introducción a GraalVM | Codemotion Madrid 2018
 
JavaScript no es Vietnam
JavaScript no es VietnamJavaScript no es Vietnam
JavaScript no es Vietnam
 
The Original Hacker número 11.
The Original Hacker número 11.The Original Hacker número 11.
The Original Hacker número 11.
 
Curso AngularJS - 7. temas avanzados
Curso AngularJS - 7. temas avanzadosCurso AngularJS - 7. temas avanzados
Curso AngularJS - 7. temas avanzados
 

Más de Jorge Vásquez

Más de Jorge Vásquez (10)

Behold! The Happy Path To Captivate Your Users With Stunning CLI Apps!
Behold! The Happy Path To Captivate Your Users With Stunning CLI Apps!Behold! The Happy Path To Captivate Your Users With Stunning CLI Apps!
Behold! The Happy Path To Captivate Your Users With Stunning CLI Apps!
 
Programación Funcional 101 con Scala y ZIO 2.0
Programación Funcional 101 con Scala y ZIO 2.0Programación Funcional 101 con Scala y ZIO 2.0
Programación Funcional 101 con Scala y ZIO 2.0
 
A Prelude of Purity: Scaling Back ZIO
A Prelude of Purity: Scaling Back ZIOA Prelude of Purity: Scaling Back ZIO
A Prelude of Purity: Scaling Back ZIO
 
Be Smart, Constrain Your Types to Free Your Brain!
Be Smart, Constrain Your Types to Free Your Brain!Be Smart, Constrain Your Types to Free Your Brain!
Be Smart, Constrain Your Types to Free Your Brain!
 
Functional Programming 101 with Scala and ZIO @FunctionalWorld
Functional Programming 101 with Scala and ZIO @FunctionalWorldFunctional Programming 101 with Scala and ZIO @FunctionalWorld
Functional Programming 101 with Scala and ZIO @FunctionalWorld
 
ZIO Prelude - ZIO World 2021
ZIO Prelude - ZIO World 2021ZIO Prelude - ZIO World 2021
ZIO Prelude - ZIO World 2021
 
Exploring type level programming in Scala
Exploring type level programming in ScalaExploring type level programming in Scala
Exploring type level programming in Scala
 
The Terror-Free Guide to Introducing Functional Scala at Work
The Terror-Free Guide to Introducing Functional Scala at WorkThe Terror-Free Guide to Introducing Functional Scala at Work
The Terror-Free Guide to Introducing Functional Scala at Work
 
Exploring ZIO Prelude: The game changer for typeclasses in Scala
Exploring ZIO Prelude: The game changer for typeclasses in ScalaExploring ZIO Prelude: The game changer for typeclasses in Scala
Exploring ZIO Prelude: The game changer for typeclasses in Scala
 
Introduction to programming with ZIO functional effects
Introduction to programming with ZIO functional effectsIntroduction to programming with ZIO functional effects
Introduction to programming with ZIO functional effects
 

Consiguiendo superpoderes para construir aplicaciones modernas en la JVM con ZIO

  • 1. Consiguiendo súperpoderes para construir aplicaciones modernas en la JVM con ZIO JVM Day Colombia 2021 6 de Noviembre 1/60
  • 5. ¿Qué es ZIO? — Librería de código abierto para Scala 4/60
  • 6. ¿Qué es ZIO? — Librería de código abierto para Scala — Permite construir aplicaciones reactivas de siguiente generación en la JVM 4/60
  • 8. Partes que constituyen ZIO — Core 5/60
  • 9. Partes que constituyen ZIO — Core — STM (So!ware Transactional Memory) 5/60
  • 10. Partes que constituyen ZIO — Core — STM (So!ware Transactional Memory) — Streams 5/60
  • 11. Partes que constituyen ZIO — Core — STM (So!ware Transactional Memory) — Streams — Test 5/60
  • 12. ¿Por qué ZIO? Porque ZIO nos ayuda a usar Programación Funcional a nivel macro, para crear aplicaciones complejas en el mundo real 6/60
  • 13. 7/60
  • 14. 8/60
  • 15. ¿Por qué ZIO? ZIO nos da súperpoderes y nos ayuda a ser más productivos a la hora de construir aplicaciones modernas: 9/60
  • 16. ¿Por qué ZIO? ZIO nos da súperpoderes y nos ayuda a ser más productivos a la hora de construir aplicaciones modernas: — Resolver problemas con menos esfuerzo 9/60
  • 17. ¿Por qué ZIO? ZIO nos da súperpoderes y nos ayuda a ser más productivos a la hora de construir aplicaciones modernas: — Resolver problemas con menos esfuerzo — Menor costo de mantenimiento 9/60
  • 18. ¿Por qué ZIO? ZIO nos da súperpoderes y nos ayuda a ser más productivos a la hora de construir aplicaciones modernas: — Resolver problemas con menos esfuerzo — Menor costo de mantenimiento — Proporciona mayores garantías en tiempo de compilación 9/60
  • 19. ZIO... Porque es Escalable 10/60
  • 20. 11/60
  • 21. 12/60
  • 22. 13/60
  • 23. ZIO... Porque es Escalable 14/60
  • 24. 15/60
  • 25. ZIO... Porque es ideal para Programación Asíncrona // Callback hell s3Get(key = key, onError = error => log(error), onSuccess = value => s3Put(key = key, value = enrichProfile(value), onError = error => log(error), onSuccess = _ => () ) ) 16/60
  • 26. ZIO... Porque es ideal para Programación Asíncrona // Callback hell s3Get(key = key, onError = error => log(error), onSuccess = value => s3Put(key = key, value = enrichProfile(value), onError = error => log(error), onSuccess = _ => () ) ) 16/60
  • 27. ZIO... Porque es ideal para Programación Asíncrona // Callback hell s3Get(key = key, onError = error => log(error), onSuccess = value => s3Put(key = key, value = enrichProfile(value), onError = error => log(error), onSuccess = _ => () ) ) 16/60
  • 28. ZIO... Porque es ideal para Programación Asíncrona // Callback hell s3Get(key = key, onError = error => log(error), onSuccess = value => s3Put(key = key, value = enrichProfile(value), onError = error => log(error), onSuccess = _ => () ) ) 16/60
  • 29. ZIO... Porque es ideal para Programación Asíncrona // Callback hell s3Get(key = key, onError = error => log(error), onSuccess = value => s3Put(key = key, value = enrichProfile(value), onError = error => log(error), onSuccess = _ => () ) ) 16/60
  • 30. ZIO... Porque es ideal para Programación Asíncrona // Callback hell s3Get(key = key, onError = error => log(error), onSuccess = value => s3Put(key = key, value = enrichProfile(value), onError = error => log(error), onSuccess = _ => () ) ) 16/60
  • 31. ZIO... Porque es ideal para Programación Asíncrona // Callback hell s3Get(key = key, onError = error => log(error), onSuccess = value => s3Put(key = key, value = enrichProfile(value), onError = error => log(error), onSuccess = _ => () ) ) 16/60
  • 32. ZIO... Porque es ideal para Programación Asíncrona // Callback hell s3Get(key = key, onError = error => log(error), onSuccess = value => s3Put(key = key, value = enrichProfile(value), onError = error => log(error), onSuccess = _ => () ) ) 16/60
  • 33. ZIO... Porque es ideal para Programación Asíncrona // Callback hell s3Get(key = key, onError = error => log(error), onSuccess = value => s3Put(key = key, value = enrichProfile(value), onError = error => log(error), onSuccess = _ => () ) ) 16/60
  • 34. 17/60
  • 35. ZIO... Porque es ideal para Programación Asíncrona // ¡Adiós callbacks! // ZIO es 100% asíncrono, pero con bloqueo semántico val enrich = (for { value <- s3Get(key) _ <- s3Put(key, enrichProfile(value)) } yield ()).catchAll(error => log(error)) 18/60
  • 36. ZIO... Porque es ideal para Programación Asíncrona // ¡Adiós callbacks! // ZIO es 100% asíncrono, pero con bloqueo semántico val enrich = (for { value <- s3Get(key) _ <- s3Put(key, enrichProfile(value)) } yield ()).catchAll(error => log(error)) 18/60
  • 37. ZIO... Porque es ideal para Programación Asíncrona // ¡Adiós callbacks! // ZIO es 100% asíncrono, pero con bloqueo semántico val enrich = (for { value <- s3Get(key) _ <- s3Put(key, enrichProfile(value)) } yield ()).catchAll(error => log(error)) 18/60
  • 38. ZIO... Porque es ideal para Programación Asíncrona // ¡Adiós callbacks! // ZIO es 100% asíncrono, pero con bloqueo semántico val enrich = (for { value <- s3Get(key) _ <- s3Put(key, enrichProfile(value)) } yield ()).catchAll(error => log(error)) 18/60
  • 39. ZIO... Porque es ideal para Programación Asíncrona // ¡Adiós callbacks! // ZIO es 100% asíncrono, pero con bloqueo semántico val enrich = (for { value <- s3Get(key) _ <- s3Put(key, enrichProfile(value)) } yield ()).catchAll(error => log(error)) 18/60
  • 40. ZIO... Porque es ideal para Programación Asíncrona // ¡Adiós callbacks! // ZIO es 100% asíncrono, pero con bloqueo semántico val enrich = (for { value <- s3Get(key) _ <- s3Put(key, enrichProfile(value)) } yield ()).catchAll(error => log(error)) 18/60
  • 41. ZIO... Porque permite maximizar el Parelelismo en una aplicación // Procesa cada URL de forma secuencial ZIO.foreach(urls) { url => for { data <- load(url) json <- parseToJson(data) transformed <- transform(json) } yield transformed } 19/60
  • 42. ZIO... Porque permite maximizar el Parelelismo en una aplicación // Procesa cada URL de forma secuencial ZIO.foreach(urls) { url => for { data <- load(url) json <- parseToJson(data) transformed <- transform(json) } yield transformed } 19/60
  • 43. ZIO... Porque permite maximizar el Parelelismo en una aplicación // Procesa cada URL de forma secuencial ZIO.foreach(urls) { url => for { data <- load(url) json <- parseToJson(data) transformed <- transform(json) } yield transformed } 19/60
  • 44. ZIO... Porque permite maximizar el Parelelismo en una aplicación // ¡Paralelizar con control preciso es trivial! ZIO.foreachParN(20)(urls) { url => for { data <- load(url) json <- parseToJson(data) transformed <- transform(json) } yield transformed } 20/60
  • 45. ZIO... Porque permite maximizar el Parelelismo en una aplicación // ¡Paralelizar con control preciso es trivial! ZIO.foreachParN(20)(urls) { url => for { data <- load(url) json <- parseToJson(data) transformed <- transform(json) } yield transformed } 20/60
  • 46. ZIO... Porque permite maximizar el Parelelismo en una aplicación // ¡Paralelizar con control preciso es trivial! ZIO.foreachParN(20)(urls) { url => for { data <- load(url) json <- parseToJson(data) transformed <- transform(json) } yield transformed } 20/60
  • 47. ZIO... Porque permite maximizar el Parelelismo en una aplicación 21/60
  • 48. ZIO... Porque permite maximizar el Parelelismo en una aplicación — Cada operador en ZIO que puede tener una versión que se ejecuta en paralelo la tiene 21/60
  • 49. ZIO... Porque permite maximizar el Parelelismo en una aplicación — Cada operador en ZIO que puede tener una versión que se ejecuta en paralelo la tiene — Estos operadores paralelos permiten controlar los límites de concurrencia 21/60
  • 50. ZIO... Por su poder para Programación Concurrente // Varios workers consumiendo una cola, // con back-pressuring automático def startWorkers(n: Int, queue: Queue[Work]) = { val worker = queue.take.flatMap(doWork).forever val workers = List.fill(n)(worker) ZIO.forkAll(workers) } 22/60
  • 51. ZIO... Por su poder para Programación Concurrente // Varios workers consumiendo una cola, // con back-pressuring automático def startWorkers(n: Int, queue: Queue[Work]) = { val worker = queue.take.flatMap(doWork).forever val workers = List.fill(n)(worker) ZIO.forkAll(workers) } 22/60
  • 52. ZIO... Por su poder para Programación Concurrente // Varios workers consumiendo una cola, // con back-pressuring automático def startWorkers(n: Int, queue: Queue[Work]) = { val worker = queue.take.flatMap(doWork).forever val workers = List.fill(n)(worker) ZIO.forkAll(workers) } 22/60
  • 53. ZIO... Por su poder para Programación Concurrente // Varios workers consumiendo una cola, // con back-pressuring automático def startWorkers(n: Int, queue: Queue[Work]) = { val worker = queue.take.flatMap(doWork).forever val workers = List.fill(n)(worker) ZIO.forkAll(workers) } 22/60
  • 54. ZIO... Por su poder para Programación Concurrente // Varios workers consumiendo una cola, // con back-pressuring automático def startWorkers(n: Int, queue: Queue[Work]) = { val worker = queue.take.flatMap(doWork).forever val workers = List.fill(n)(worker) ZIO.forkAll(workers) } 22/60
  • 55. ZIO... Por su poder para Programación Concurrente // Varios workers consumiendo una cola, // con back-pressuring automático def startWorkers(n: Int, queue: Queue[Work]) = { val worker = queue.take.flatMap(doWork).forever val workers = List.fill(n)(worker) ZIO.forkAll(workers) } 22/60
  • 56. ZIO... Por su poder para Programación Concurrente // Hacer commit de transacciones condicionales sin Locks o // Condition Variables, sin condiciones de carrera ni deadlocks: def acquireConnection(available: TRef[List[Connection]], used: TRef[List[Connection]]) = STM.atomically { for { // Si no se encuentra una conexión disponible, se hace un rollback de la transacción // y se espera hasta que haya una disponible para reintentar connection <- available.get.collect { case head :: _ => head } _ <- available.update(_.drop(1)) _ <- used.update(connection :: _) } yield connection } 23/60
  • 57. ZIO... Por su poder para Programación Concurrente // Hacer commit de transacciones condicionales sin Locks o // Condition Variables, sin condiciones de carrera ni deadlocks: def acquireConnection(available: TRef[List[Connection]], used: TRef[List[Connection]]) = STM.atomically { for { // Si no se encuentra una conexión disponible, se hace un rollback de la transacción // y se espera hasta que haya una disponible para reintentar connection <- available.get.collect { case head :: _ => head } _ <- available.update(_.drop(1)) _ <- used.update(connection :: _) } yield connection } 23/60
  • 58. ZIO... Por su poder para Programación Concurrente // Hacer commit de transacciones condicionales sin Locks o // Condition Variables, sin condiciones de carrera ni deadlocks: def acquireConnection(available: TRef[List[Connection]], used: TRef[List[Connection]]) = STM.atomically { for { // Si no se encuentra una conexión disponible, se hace un rollback de la transacción // y se espera hasta que haya una disponible para reintentar connection <- available.get.collect { case head :: _ => head } _ <- available.update(_.drop(1)) _ <- used.update(connection :: _) } yield connection } 23/60
  • 59. ZIO... Por su poder para Programación Concurrente // Hacer commit de transacciones condicionales sin Locks o // Condition Variables, sin condiciones de carrera ni deadlocks: def acquireConnection(available: TRef[List[Connection]], used: TRef[List[Connection]]) = STM.atomically { for { // Si no se encuentra una conexión disponible, se hace un rollback de la transacción // y se espera hasta que haya una disponible para reintentar connection <- available.get.collect { case head :: _ => head } _ <- available.update(_.drop(1)) _ <- used.update(connection :: _) } yield connection } 23/60
  • 60. ZIO... Por su poder para Programación Concurrente // Hacer commit de transacciones condicionales sin Locks o // Condition Variables, sin condiciones de carrera ni deadlocks: def acquireConnection(available: TRef[List[Connection]], used: TRef[List[Connection]]) = STM.atomically { for { // Si no se encuentra una conexión disponible, se hace un rollback de la transacción // y se espera hasta que haya una disponible para reintentar connection <- available.get.collect { case head :: _ => head } _ <- available.update(_.drop(1)) _ <- used.update(connection :: _) } yield connection } 23/60
  • 61. ZIO... Por su poder para Programación Concurrente // Hacer commit de transacciones condicionales sin Locks o // Condition Variables, sin condiciones de carrera ni deadlocks: def acquireConnection(available: TRef[List[Connection]], used: TRef[List[Connection]]) = STM.atomically { for { // Si no se encuentra una conexión disponible, se hace un rollback de la transacción // y se espera hasta que haya una disponible para reintentar connection <- available.get.collect { case head :: _ => head } _ <- available.update(_.drop(1)) _ <- used.update(connection :: _) } yield connection } 23/60
  • 62. ZIO... Por su poder para Programación Concurrente // Hacer commit de transacciones condicionales sin Locks o // Condition Variables, sin condiciones de carrera ni deadlocks: def acquireConnection(available: TRef[List[Connection]], used: TRef[List[Connection]]) = STM.atomically { for { // Si no se encuentra una conexión disponible, se hace un rollback de la transacción // y se espera hasta que haya una disponible para reintentar connection <- available.get.collect { case head :: _ => head } _ <- available.update(_.drop(1)) _ <- used.update(connection :: _) } yield connection } 23/60
  • 63. ZIO... Por su poder para Programación Concurrente // Hacer commit de transacciones condicionales sin Locks o // Condition Variables, sin condiciones de carrera ni deadlocks: def acquireConnection(available: TRef[List[Connection]], used: TRef[List[Connection]]) = STM.atomically { for { // Si no se encuentra una conexión disponible, se hace un rollback de la transacción // y se espera hasta que haya una disponible para reintentar connection <- available.get.collect { case head :: _ => head } _ <- available.update(_.drop(1)) _ <- used.update(connection :: _) } yield connection } 23/60
  • 64. ZIO... Porque permite eliminar leaks de recursos // Empaquetamos la lógica de adquisición y liberación de un recurso usando Managed, // eliminando cualquier posibilidad de leaks val managedFile = Managed.make(open(file))(close(_)) // Modularidad: La lógica de uso de un recurso está separada de la lógica // de adquisición y liberación del mismo managedFile.use { resource => (for { data <- read(resource) _ <- aggregateData(data) } yield ()).forever } 24/60
  • 65. ZIO... Porque permite eliminar leaks de recursos // Empaquetamos la lógica de adquisición y liberación de un recurso usando Managed, // eliminando cualquier posibilidad de leaks val managedFile = Managed.make(open(file))(close(_)) // Modularidad: La lógica de uso de un recurso está separada de la lógica // de adquisición y liberación del mismo managedFile.use { resource => (for { data <- read(resource) _ <- aggregateData(data) } yield ()).forever } 24/60
  • 66. ZIO... Porque permite eliminar leaks de recursos // Empaquetamos la lógica de adquisición y liberación de un recurso usando Managed, // eliminando cualquier posibilidad de leaks val managedFile = Managed.make(open(file))(close(_)) // Modularidad: La lógica de uso de un recurso está separada de la lógica // de adquisición y liberación del mismo managedFile.use { resource => (for { data <- read(resource) _ <- aggregateData(data) } yield ()).forever } 24/60
  • 67. ZIO... Porque permite eliminar leaks de recursos // Empaquetamos la lógica de adquisición y liberación de un recurso usando Managed, // eliminando cualquier posibilidad de leaks val managedFile = Managed.make(open(file))(close(_)) // Modularidad: La lógica de uso de un recurso está separada de la lógica // de adquisición y liberación del mismo managedFile.use { resource => (for { data <- read(resource) _ <- aggregateData(data) } yield ()).forever } 24/60
  • 68. ZIO... Porque permite eliminar leaks de recursos // Empaquetamos la lógica de adquisición y liberación de un recurso usando Managed, // eliminando cualquier posibilidad de leaks val managedFile = Managed.make(open(file))(close(_)) // Modularidad: La lógica de uso de un recurso está separada de la lógica // de adquisición y liberación del mismo managedFile.use { resource => (for { data <- read(resource) _ <- aggregateData(data) } yield ()).forever } 24/60
  • 69. ZIO... Porque permite eliminar leaks de recursos // Combinar varios ZManaged en uno solo val managedFiles = ZManaged.foreach(files){ file => Managed.make(open(file))(close(_)) } // Todos los archivos se abren y se cierran al mismo tiempo, // de forma automática managedFiles.use { resources => ZIO.foreach(resources) { resource => (for { data <- read(resource) _ <- aggregateData(data) } yield ()).forever } } 25/60
  • 70. ZIO... Porque permite eliminar leaks de recursos // Combinar varios ZManaged en uno solo val managedFiles = ZManaged.foreach(files){ file => Managed.make(open(file))(close(_)) } // Todos los archivos se abren y se cierran al mismo tiempo, // de forma automática managedFiles.use { resources => ZIO.foreach(resources) { resource => (for { data <- read(resource) _ <- aggregateData(data) } yield ()).forever } } 25/60
  • 71. ZIO... Porque permite eliminar leaks de recursos // Combinar varios ZManaged en uno solo val managedFiles = ZManaged.foreach(files){ file => Managed.make(open(file))(close(_)) } // Todos los archivos se abren y se cierran al mismo tiempo, // de forma automática managedFiles.use { resources => ZIO.foreach(resources) { resource => (for { data <- read(resource) _ <- aggregateData(data) } yield ()).forever } } 25/60
  • 72. ZIO... Porque permite eliminar leaks de recursos // Combinar varios ZManaged en uno solo val managedFiles = ZManaged.foreach(files){ file => Managed.make(open(file))(close(_)) } // Todos los archivos se abren y se cierran al mismo tiempo, // de forma automática managedFiles.use { resources => ZIO.foreach(resources) { resource => (for { data <- read(resource) _ <- aggregateData(data) } yield ()).forever } } 25/60
  • 73. ZIO... Porque permite eliminar leaks de recursos // Combinar varios ZManaged en uno solo val managedFiles = ZManaged.foreach(files){ file => Managed.make(open(file))(close(_)) } // Todos los archivos se abren y se cierran al mismo tiempo, // de forma automática managedFiles.use { resources => ZIO.foreach(resources) { resource => (for { data <- read(resource) _ <- aggregateData(data) } yield ()).forever } } 25/60
  • 74. ZIO... Porque es Globalmente Eficiente, automáticamente val geoLookup = geoIpService.lookup(ipAddress) val dbLookup = userRepo.getProfile(userId).map(_.location.toLatLong) // Hacer que dos efectos compitan en paralelo, cancelando el perdedor val fastest = geoLookup.race(dbLookup) 26/60
  • 75. ZIO... Porque es Globalmente Eficiente, automáticamente val geoLookup = geoIpService.lookup(ipAddress) val dbLookup = userRepo.getProfile(userId).map(_.location.toLatLong) // Hacer que dos efectos compitan en paralelo, cancelando el perdedor val fastest = geoLookup.race(dbLookup) 26/60
  • 76. ZIO... Porque es Globalmente Eficiente, automáticamente val geoLookup = geoIpService.lookup(ipAddress) val dbLookup = userRepo.getProfile(userId).map(_.location.toLatLong) // Hacer que dos efectos compitan en paralelo, cancelando el perdedor val fastest = geoLookup.race(dbLookup) 26/60
  • 77. ZIO... Porque es Globalmente Eficiente, automáticamente val geoLookup = geoIpService.lookup(ipAddress) val dbLookup = userRepo.getProfile(userId).map(_.location.toLatLong) // Hacer que dos efectos compitan en paralelo, cancelando el perdedor val fastest = geoLookup.race(dbLookup) 26/60
  • 78. ZIO... Porque es Globalmente Eficiente, automáticamente val geoLookup = geoIpService.lookup(ipAddress) val dbLookup = userRepo.getProfile(userId).map(_.location.toLatLong) // Hacer que dos efectos corran en paralelo, // Si uno de ellos falla, el otro es automáticamente interrumpido val fastest = geoLookup.zipPar(dbLookup) 27/60
  • 79. ZIO... Porque es Globalmente Eficiente, automáticamente val geoLookup = geoIpService.lookup(ipAddress) val dbLookup = userRepo.getProfile(userId).map(_.location.toLatLong) // Hacer que dos efectos corran en paralelo, // Si uno de ellos falla, el otro es automáticamente interrumpido val fastest = geoLookup.zipPar(dbLookup) 27/60
  • 80. ZIO... Porque es Globalmente Eficiente, automáticamente val geoLookup = geoIpService.lookup(ipAddress) val dbLookup = userRepo.getProfile(userId).map(_.location.toLatLong) // Hacer que dos efectos corran en paralelo, // Si uno de ellos falla, el otro es automáticamente interrumpido val fastest = geoLookup.zipPar(dbLookup) 27/60
  • 81. ZIO... Porque es Globalmente Eficiente, automáticamente val geoLookup = geoIpService.lookup(ipAddress) val dbLookup = userRepo.getProfile(userId).map(_.location.toLatLong) // Hacer que dos efectos corran en paralelo, // Si uno de ellos falla, el otro es automáticamente interrumpido val fastest = geoLookup.zipPar(dbLookup) 27/60
  • 82. ZIO... Porque es Globalmente Eficiente, automáticamente val slowDbQuery = userRepo.getProfile(userId) // Cancelar efectos que se ejecutan lentamente, después de un timeout determinado slowDbQuery.timeout(60.seconds) 28/60
  • 83. ZIO... Porque es Globalmente Eficiente, automáticamente val slowDbQuery = userRepo.getProfile(userId) // Cancelar efectos que se ejecutan lentamente, después de un timeout determinado slowDbQuery.timeout(60.seconds) 28/60
  • 84. ZIO... Porque es Globalmente Eficiente, automáticamente val slowDbQuery = userRepo.getProfile(userId) // Cancelar efectos que se ejecutan lentamente, después de un timeout determinado slowDbQuery.timeout(60.seconds) 28/60
  • 85. ZIO... Porque es Globalmente Eficiente, automáticamente val slowDbQuery = userRepo.getProfile(userId) // Cancelar efectos que se ejecutan lentamente, después de un timeout determinado slowDbQuery.timeout(60.seconds) 28/60
  • 86. ZIO... Porque permite operar sobre Streams infinitos de datos // ZIO Streams nos permite construir pipelines complejos // que operen sobre flujos de datos posiblemente infinitos, // de forma declarativa, concurrente, leak-free y usando // un espacio constante de memoria. val wordCount = ZStream .fromInputStream(Files.newInputStream(path)) .transduce(ZTransducer.utf8Decode) .transduce(ZTransducer.splitOn(" ")) .run(ZSink.count) 29/60
  • 87. ZIO... Porque permite operar sobre Streams infinitos de datos // ZIO Streams nos permite construir pipelines complejos // que operen sobre flujos de datos posiblemente infinitos, // de forma declarativa, concurrente, leak-free y usando // un espacio constante de memoria. val wordCount = ZStream .fromInputStream(Files.newInputStream(path)) .transduce(ZTransducer.utf8Decode) .transduce(ZTransducer.splitOn(" ")) .run(ZSink.count) 29/60
  • 88. ZIO... Porque permite operar sobre Streams infinitos de datos // ZIO Streams nos permite construir pipelines complejos // que operen sobre flujos de datos posiblemente infinitos, // de forma declarativa, concurrente, leak-free y usando // un espacio constante de memoria. val wordCount = ZStream .fromInputStream(Files.newInputStream(path)) .transduce(ZTransducer.utf8Decode) .transduce(ZTransducer.splitOn(" ")) .run(ZSink.count) 29/60
  • 89. ZIO... Porque permite operar sobre Streams infinitos de datos // ZIO Streams nos permite construir pipelines complejos // que operen sobre flujos de datos posiblemente infinitos, // de forma declarativa, concurrente, leak-free y usando // un espacio constante de memoria. val wordCount = ZStream .fromInputStream(Files.newInputStream(path)) .transduce(ZTransducer.utf8Decode) .transduce(ZTransducer.splitOn(" ")) .run(ZSink.count) 29/60
  • 90. ZIO... Porque permite operar sobre Streams infinitos de datos // ZIO Streams nos permite construir pipelines complejos // que operen sobre flujos de datos posiblemente infinitos, // de forma declarativa, concurrente, leak-free y usando // un espacio constante de memoria. val wordCount = ZStream .fromInputStream(Files.newInputStream(path)) .transduce(ZTransducer.utf8Decode) .transduce(ZTransducer.splitOn(" ")) .run(ZSink.count) 29/60
  • 91. ZIO... Porque permite operar sobre Streams infinitos de datos // ZIO Streams nos permite construir pipelines complejos // que operen sobre flujos de datos posiblemente infinitos, // de forma declarativa, concurrente, leak-free y usando // un espacio constante de memoria. val wordCount = ZStream .fromInputStream(Files.newInputStream(path)) .transduce(ZTransducer.utf8Decode) .transduce(ZTransducer.splitOn(" ")) .run(ZSink.count) 29/60
  • 92. ZIO... Porque nos permite crear aplicaciones fáciles de testear // ZIO permite alta testabilidad a través del ZIO Environment, el cual hace que sea fácil para los desarrolladores // seguir buenas prácticas de Programación Orientada a Objetos, por ejemplo: // "Codificar contra interfaces, no implementaciones". // Además, ZIO nos permite escribir tests que sean rápidos y determinísticos, // incluso para aplicaciones interactivas, no determinísticas val program: ZIO[Has[Clock] with Has[Console], IOException, Unit] = for { _ <- putStrLn("What is your name?") name <- getStrLn _ <- putStrLn(s"I will wait ${name.length} seconds, $name") _ <- ZIO.sleep(name.length.seconds) } yield () // testServices contiene implementaciones de prueba para Clock y Console val deterministicResult = program.provideLayer(testServices) 30/60
  • 93. ZIO... Porque nos permite crear aplicaciones fáciles de testear // ZIO permite alta testabilidad a través del ZIO Environment, el cual hace que sea fácil para los desarrolladores // seguir buenas prácticas de Programación Orientada a Objetos, por ejemplo: // "Codificar contra interfaces, no implementaciones". // Además, ZIO nos permite escribir tests que sean rápidos y determinísticos, // incluso para aplicaciones interactivas, no determinísticas val program: ZIO[Has[Clock] with Has[Console], IOException, Unit] = for { _ <- putStrLn("What is your name?") name <- getStrLn _ <- putStrLn(s"I will wait ${name.length} seconds, $name") _ <- ZIO.sleep(name.length.seconds) } yield () // testServices contiene implementaciones de prueba para Clock y Console val deterministicResult = program.provideLayer(testServices) 30/60
  • 94. ZIO... Porque nos permite crear aplicaciones fáciles de testear // ZIO permite alta testabilidad a través del ZIO Environment, el cual hace que sea fácil para los desarrolladores // seguir buenas prácticas de Programación Orientada a Objetos, por ejemplo: // "Codificar contra interfaces, no implementaciones". // Además, ZIO nos permite escribir tests que sean rápidos y determinísticos, // incluso para aplicaciones interactivas, no determinísticas val program: ZIO[Has[Clock] with Has[Console], IOException, Unit] = for { _ <- putStrLn("What is your name?") name <- getStrLn _ <- putStrLn(s"I will wait ${name.length} seconds, $name") _ <- ZIO.sleep(name.length.seconds) } yield () // testServices contiene implementaciones de prueba para Clock y Console val deterministicResult = program.provideLayer(testServices) 30/60
  • 95. ZIO... Porque nos permite crear aplicaciones fáciles de testear // ZIO permite alta testabilidad a través del ZIO Environment, el cual hace que sea fácil para los desarrolladores // seguir buenas prácticas de Programación Orientada a Objetos, por ejemplo: // "Codificar contra interfaces, no implementaciones". // Además, ZIO nos permite escribir tests que sean rápidos y determinísticos, // incluso para aplicaciones interactivas, no determinísticas val program: ZIO[Has[Clock] with Has[Console], IOException, Unit] = for { _ <- putStrLn("What is your name?") name <- getStrLn _ <- putStrLn(s"I will wait ${name.length} seconds, $name") _ <- ZIO.sleep(name.length.seconds) } yield () // testServices contiene implementaciones de prueba para Clock y Console val deterministicResult = program.provideLayer(testServices) 30/60
  • 96. ZIO... Porque nos permite crear aplicaciones fáciles de testear // ZIO permite alta testabilidad a través del ZIO Environment, el cual hace que sea fácil para los desarrolladores // seguir buenas prácticas de Programación Orientada a Objetos, por ejemplo: // "Codificar contra interfaces, no implementaciones". // Además, ZIO nos permite escribir tests que sean rápidos y determinísticos, // incluso para aplicaciones interactivas, no determinísticas val program: ZIO[Has[Clock] with Has[Console], IOException, Unit] = for { _ <- putStrLn("What is your name?") name <- getStrLn _ <- putStrLn(s"I will wait ${name.length} seconds, $name") _ <- ZIO.sleep(name.length.seconds) } yield () // testServices contiene implementaciones de prueba para Clock y Console val deterministicResult = program.provideLayer(testServices) 30/60
  • 97. ZIO... Porque nos permite crear aplicaciones resilientes // Deja que el compilador te diga qué puede fallar, y por qué val result: IO[RequestException, Result] = callFlakyApi(request) // ZIO aprovecha al máximo el poder del compilador de Scala, // para que guiados por él podamos construir aplicaciones resilientes // Por ejemplo, ZIO nos permite introducir reintentos que se ejecuten // de acuerdo a un Schedule específico val retryPolicy = (Schedule.exponential(10.millis).whileOutput(_ < 1.second) andThen Schedule.spaced(60.seconds) ) && Schedule.recurs(100) val robustResult: IO[RequestException, Result] = result.retry(retryPolicy) // Deja que el compilador te diga qué NO puede fallar val infallible: IO[Nothing, Result] = robustResult.catchAll(_ => fallback) 31/60
  • 98. ZIO... Porque nos permite crear aplicaciones resilientes // Deja que el compilador te diga qué puede fallar, y por qué val result: IO[RequestException, Result] = callFlakyApi(request) // ZIO aprovecha al máximo el poder del compilador de Scala, // para que guiados por él podamos construir aplicaciones resilientes // Por ejemplo, ZIO nos permite introducir reintentos que se ejecuten // de acuerdo a un Schedule específico val retryPolicy = (Schedule.exponential(10.millis).whileOutput(_ < 1.second) andThen Schedule.spaced(60.seconds) ) && Schedule.recurs(100) val robustResult: IO[RequestException, Result] = result.retry(retryPolicy) // Deja que el compilador te diga qué NO puede fallar val infallible: IO[Nothing, Result] = robustResult.catchAll(_ => fallback) 31/60
  • 99. ZIO... Porque nos permite crear aplicaciones resilientes // Deja que el compilador te diga qué puede fallar, y por qué val result: IO[RequestException, Result] = callFlakyApi(request) // ZIO aprovecha al máximo el poder del compilador de Scala, // para que guiados por él podamos construir aplicaciones resilientes // Por ejemplo, ZIO nos permite introducir reintentos que se ejecuten // de acuerdo a un Schedule específico val retryPolicy = (Schedule.exponential(10.millis).whileOutput(_ < 1.second) andThen Schedule.spaced(60.seconds) ) && Schedule.recurs(100) val robustResult: IO[RequestException, Result] = result.retry(retryPolicy) // Deja que el compilador te diga qué NO puede fallar val infallible: IO[Nothing, Result] = robustResult.catchAll(_ => fallback) 31/60
  • 100. ZIO... Porque nos permite crear aplicaciones resilientes // Deja que el compilador te diga qué puede fallar, y por qué val result: IO[RequestException, Result] = callFlakyApi(request) // ZIO aprovecha al máximo el poder del compilador de Scala, // para que guiados por él podamos construir aplicaciones resilientes // Por ejemplo, ZIO nos permite introducir reintentos que se ejecuten // de acuerdo a un Schedule específico val retryPolicy = (Schedule.exponential(10.millis).whileOutput(_ < 1.second) andThen Schedule.spaced(60.seconds) ) && Schedule.recurs(100) val robustResult: IO[RequestException, Result] = result.retry(retryPolicy) // Deja que el compilador te diga qué NO puede fallar val infallible: IO[Nothing, Result] = robustResult.catchAll(_ => fallback) 31/60
  • 101. ZIO... Porque nos permite crear aplicaciones resilientes // Deja que el compilador te diga qué puede fallar, y por qué val result: IO[RequestException, Result] = callFlakyApi(request) // ZIO aprovecha al máximo el poder del compilador de Scala, // para que guiados por él podamos construir aplicaciones resilientes // Por ejemplo, ZIO nos permite introducir reintentos que se ejecuten // de acuerdo a un Schedule específico val retryPolicy = (Schedule.exponential(10.millis).whileOutput(_ < 1.second) andThen Schedule.spaced(60.seconds) ) && Schedule.recurs(100) val robustResult: IO[RequestException, Result] = result.retry(retryPolicy) // Deja que el compilador te diga qué NO puede fallar val infallible: IO[Nothing, Result] = robustResult.catchAll(_ => fallback) 31/60
  • 102. ZIO... Porque nos permite crear aplicaciones resilientes // Deja que el compilador te diga qué puede fallar, y por qué val result: IO[RequestException, Result] = callFlakyApi(request) // ZIO aprovecha al máximo el poder del compilador de Scala, // para que guiados por él podamos construir aplicaciones resilientes // Por ejemplo, ZIO nos permite introducir reintentos que se ejecuten // de acuerdo a un Schedule específico val retryPolicy = (Schedule.exponential(10.millis).whileOutput(_ < 1.second) andThen Schedule.spaced(60.seconds) ) && Schedule.recurs(100) val robustResult: IO[RequestException, Result] = result.retry(retryPolicy) // Deja que el compilador te diga qué NO puede fallar val infallible: IO[Nothing, Result] = robustResult.catchAll(_ => fallback) 31/60
  • 103. ZIO... Porque nos permite crear aplicaciones resilientes // Deja que el compilador te diga qué puede fallar, y por qué val result: IO[RequestException, Result] = callFlakyApi(request) // ZIO aprovecha al máximo el poder del compilador de Scala, // para que guiados por él podamos construir aplicaciones resilientes // Por ejemplo, ZIO nos permite introducir reintentos que se ejecuten // de acuerdo a un Schedule específico val retryPolicy = (Schedule.exponential(10.millis).whileOutput(_ < 1.second) andThen Schedule.spaced(60.seconds) ) && Schedule.recurs(100) val robustResult: IO[RequestException, Result] = result.retry(retryPolicy) // Deja que el compilador te diga qué NO puede fallar val infallible: IO[Nothing, Result] = robustResult.catchAll(_ => fallback) 31/60
  • 104. ZIO... Porque es altamente Composicional 32/60
  • 105. ZIO... Porque es altamente Composicional val managedData = Managed.make(open(url))(close(_)) managedData.use { data => searchBreadth(data) } 33/60
  • 106. 34/60
  • 107. ZIO... Porque es altamente Composicional ZIO.foreach(urls) { url => val managedData = Managed.make(open(url))(close(_)) managedData.use { data => searchBreadth(data) } } 35/60
  • 108. ZIO... Porque es altamente Composicional ZIO.foreach(urls) { url => val managedData = Managed.make(open(url))(close(_)) managedData.use { data => searchBreadth(data) } } 35/60
  • 109. 36/60
  • 110. ZIO... Porque es altamente Composicional ZIO.foreachPar(urls) { url => val managedData = Managed.make(open(url))(close(_)) managedData.use { data => searchBreadth(data) } } 37/60
  • 111. ZIO... Porque es altamente Composicional ZIO.foreachPar(urls) { url => val managedData = Managed.make(open(url))(close(_)) managedData.use { data => searchBreadth(data) } } 37/60
  • 112. 38/60
  • 113. ZIO... Porque es altamente Composicional ZIO.foreachParN(20)(urls) { url => val managedData = Managed.make(open(url))(close(_)) managedData.use { data => searchBreadth(data) } } 39/60
  • 114. ZIO... Porque es altamente Composicional ZIO.foreachParN(20)(urls) { url => val managedData = Managed.make(open(url))(close(_)) managedData.use { data => searchBreadth(data) } } 39/60
  • 115. 40/60
  • 116. ZIO... Porque es altamente Composicional val policy = Schedule.recurs(100) ZIO.foreachParN(20)(urls) { url => val managedData = Managed.make(open(url))(close(_)) val robustData = managedData.retry(policy) robustData.use { data => searchBreadth(data) } } 41/60
  • 117. ZIO... Porque es altamente Composicional val policy = Schedule.recurs(100) ZIO.foreachParN(20)(urls) { url => val managedData = Managed.make(open(url))(close(_)) val robustData = managedData.retry(policy) robustData.use { data => searchBreadth(data) } } 41/60
  • 118. ZIO... Porque es altamente Composicional val policy = Schedule.recurs(100) ZIO.foreachParN(20)(urls) { url => val managedData = Managed.make(open(url))(close(_)) val robustData = managedData.retry(policy) robustData.use { data => searchBreadth(data) } } 41/60
  • 119. 42/60
  • 120. ZIO... Porque es altamente Composicional val policy = Schedule.recurs(100) && Schedule.exponential(10.millis) ZIO.foreachParN(20)(urls) { url => val managedData = Managed.make(open(url))(close(_)) val robustData = managedData.retry(policy) robustData.use { data => searchBreadth(data) } } 43/60
  • 121. ZIO... Porque es altamente Composicional val policy = Schedule.recurs(100) && Schedule.exponential(10.millis) ZIO.foreachParN(20)(urls) { url => val managedData = Managed.make(open(url))(close(_)) val robustData = managedData.retry(policy) robustData.use { data => searchBreadth(data) } } 43/60
  • 122. 44/60
  • 123. ZIO... Porque es altamente Composicional val policy = Schedule.recurs(100) && Schedule.exponential(10.millis) ZIO.foreachParN(20)(urls) { url => val managedData = Managed.make(open(url))(close(_)) val robustData = managedData.retry(policy).timeoutFail(30.seconds) robustData.use { data => searchBreadth(data) } } 45/60
  • 124. ZIO... Porque es altamente Composicional val policy = Schedule.recurs(100) && Schedule.exponential(10.millis) ZIO.foreachParN(20)(urls) { url => val managedData = Managed.make(open(url))(close(_)) val robustData = managedData.retry(policy).timeoutFail(30.seconds) robustData.use { data => searchBreadth(data) } } 45/60
  • 125. 46/60
  • 126. ZIO... Porque es altamente Composicional val policy = Schedule.recurs(100) && Schedule.exponential(10.millis) ZIO.foreachParN(20)(urls) { url => val managedData = Managed.make(open(url))(close(_)) val robustData = managedData.retry(policy).timeoutFail(30.seconds) robustData.use { data => searchBreadth(data).race(searchDepth(data)) } } 47/60
  • 127. ZIO... Porque es altamente Composicional val policy = Schedule.recurs(100) && Schedule.exponential(10.millis) ZIO.foreachParN(20)(urls) { url => val managedData = Managed.make(open(url))(close(_)) val robustData = managedData.retry(policy).timeoutFail(30.seconds) robustData.use { data => searchBreadth(data).race(searchDepth(data)) } } 47/60
  • 128. 48/60
  • 129. ZIO... Porque es altamente Composicional val policy = Schedule.recurs(100) && Schedule.exponential(10.millis) ZIO.foreachParN(20)(urls) { url => val managedData = Managed.make(open(url))(close(_)) val robustData = managedData.retry(policy).timeoutFail(30.seconds) robustData.use { data => searchBreadth(data).race(searchDepth(data)) } }.timeout(10.minutes) 49/60
  • 130. ZIO... Porque es altamente Composicional val policy = Schedule.recurs(100) && Schedule.exponential(10.millis) ZIO.foreachParN(20)(urls) { url => val managedData = Managed.make(open(url))(close(_)) val robustData = managedData.retry(policy).timeoutFail(30.seconds) robustData.use { data => searchBreadth(data).race(searchDepth(data)) } }.timeout(10.minutes) 49/60
  • 131. ZIO... Porque es altamente Composicional val policy = Schedule.recurs(100) && Schedule.exponential(10.millis) ZIO.foreachParN(20)(urls) { url => val managedData = Managed.make(open(url))(close(_)) val robustData = managedData.retry(policy).timeoutFail(30.seconds) robustData.use { data => searchBreadth(data).race(searchDepth(data)) } }.timeout(10.minutes) 49/60
  • 132. 50/60
  • 133. 51/60
  • 134. 52/60
  • 135. Mejoras que serán introducidas en ZIO 2.0 53/60
  • 136. Mejoras que serán introducidas en ZIO 2.0 — Mejor performance 53/60
  • 137. Mejoras que serán introducidas en ZIO 2.0 — Mejor performance — Mejor Fiber Scheduler 53/60
  • 138. Mejoras que serán introducidas en ZIO 2.0 — Mejor performance — Mejor Fiber Scheduler — API más ergonómica 53/60
  • 139. Mejoras que serán introducidas en ZIO 2.0 54/60
  • 140. Mejoras que serán introducidas en ZIO 2.0 — Profiler incorporado 54/60
  • 141. Mejoras que serán introducidas en ZIO 2.0 — Profiler incorporado — Mejor execution tracing, con prácticamente cero overhead 54/60
  • 142. Mejoras que serán introducidas en ZIO 2.0 — Profiler incorporado — Mejor execution tracing, con prácticamente cero overhead — Métricas incorporadas, con soporte por defecto para: 54/60
  • 143. Mejoras que serán introducidas en ZIO 2.0 — Profiler incorporado — Mejor execution tracing, con prácticamente cero overhead — Métricas incorporadas, con soporte por defecto para: — Datadog 54/60
  • 144. Mejoras que serán introducidas en ZIO 2.0 — Profiler incorporado — Mejor execution tracing, con prácticamente cero overhead — Métricas incorporadas, con soporte por defecto para: — Datadog — StatsD 54/60
  • 145. Mejoras que serán introducidas en ZIO 2.0 — Profiler incorporado — Mejor execution tracing, con prácticamente cero overhead — Métricas incorporadas, con soporte por defecto para: — Datadog — StatsD — Prometheus 54/60
  • 146. Mejoras que serán introducidas en ZIO 2.0 — Profiler incorporado — Mejor execution tracing, con prácticamente cero overhead — Métricas incorporadas, con soporte por defecto para: — Datadog — StatsD — Prometheus — New Relic 54/60
  • 147. El Ecosistema de ZIO 55/60
  • 148. El Ecosistema de ZIO — ZIO CLI 55/60
  • 149. El Ecosistema de ZIO — ZIO CLI — ZIO Cache 55/60
  • 150. El Ecosistema de ZIO — ZIO CLI — ZIO Cache — ZIO Json 55/60
  • 151. El Ecosistema de ZIO — ZIO CLI — ZIO Cache — ZIO Json — ZIO Http 55/60
  • 152. El Ecosistema de ZIO — ZIO CLI — ZIO Cache — ZIO Json — ZIO Http — Caliban 55/60
  • 153. El Ecosistema de ZIO 56/60
  • 154. El Ecosistema de ZIO — ZIO Microservices 56/60
  • 155. El Ecosistema de ZIO — ZIO Microservices — ZIO gRPC 56/60
  • 156. El Ecosistema de ZIO — ZIO Microservices — ZIO gRPC — ZIO Flow 56/60
  • 157. El Ecosistema de ZIO — ZIO Microservices — ZIO gRPC — ZIO Flow — ZIO Quill 56/60
  • 158. El Ecosistema de ZIO — ZIO Microservices — ZIO gRPC — ZIO Flow — ZIO Quill — ZIO SQL 56/60
  • 159. El Ecosistema de ZIO 57/60
  • 160. El Ecosistema de ZIO — ZIO Query 57/60
  • 161. El Ecosistema de ZIO — ZIO Query — ZIO Kafka 57/60
  • 162. El Ecosistema de ZIO — ZIO Query — ZIO Kafka — ZIO Pulsar 57/60
  • 163. El Ecosistema de ZIO — ZIO Query — ZIO Kafka — ZIO Pulsar — ZIO AWS 57/60
  • 164. El Ecosistema de ZIO — ZIO Query — ZIO Kafka — ZIO Pulsar — ZIO AWS — ZIO Redis 57/60
  • 165. El Ecosistema de ZIO — ZIO Query — ZIO Kafka — ZIO Pulsar — ZIO AWS — ZIO Redis — Y muchas otras más... 57/60
  • 166. Dónde encontrar más info acerca de ZIO 58/60
  • 167. Dónde encontrar más info acerca de ZIO — Página oficial de ZIO 58/60
  • 168. Dónde encontrar más info acerca de ZIO — Página oficial de ZIO — ZIO en GitHub 58/60
  • 169. Dónde encontrar más info acerca de ZIO — Página oficial de ZIO — ZIO en GitHub — Zionomicon 58/60
  • 170. Dónde encontrar más info acerca de ZIO — Página oficial de ZIO — ZIO en GitHub — Zionomicon — "The ZIO of the Future", charla por John De Goes (autor de ZIO) 58/60
  • 171. Dónde encontrar más info acerca de ZIO 59/60
  • 172. Dónde encontrar más info acerca de ZIO — Introducción a la Programación con Efectos Funcionales usando ZIO 59/60
  • 173. Dónde encontrar más info acerca de ZIO — Introducción a la Programación con Efectos Funcionales usando ZIO — Functional Programming 101 with Scala and ZIO 59/60
  • 174. Dónde encontrar más info acerca de ZIO — Introducción a la Programación con Efectos Funcionales usando ZIO — Functional Programming 101 with Scala and ZIO — Mastering Modularity in ZIO with ZLayer 59/60
  • 175. Dónde encontrar más info acerca de ZIO — Introducción a la Programación con Efectos Funcionales usando ZIO — Functional Programming 101 with Scala and ZIO — Mastering Modularity in ZIO with ZLayer — How to write a (completely lock-free) concurrent LRU Cache with ZIO STM 59/60