lunes, 28 de febrero de 2022

Descubrir un servicio publicado con Eureka con Spring boot, parte 3


Seguimos con el post anterior.

Veamos un ejemplo de cómo usar un RestTemplate que es Ribbonaware. Este es uno de los mecanismos más comunes para interactuar con Ribbon a través de Spring. Para usar una clase RestTemplate con un balanceador de carga, debe definir un método de construcción de bean RestTemplate con una anotación de Spring Cloud llamada @LoadBalanced. 

El siguiente código muestra el método getRestTemplate() que creará el bean Spring RestTemplate respaldado por Ribbon.

package com.thoughtmechanix.licenses;

//...Most of import statements have been removed for consiceness

import org.springframework.cloud.client.loadbalancer.LoadBalanced;

import org.springframework.context.annotation.Bean;

import org.springframework.web.client.RestTemplate;

@SpringBootApplication

@EnableDiscoveryClient

@EnableFeignClients

public class Application {

  @LoadBalanced
  @Bean
  public RestTemplate getRestTemplate(){
    return new RestTemplate();
  }

  public static void main(String[] args) {
    SpringApplication.run(Application.class, args);
  }
}

Ahora que la definición del bean para la clase RestTemplate respaldada por Ribbon está definida, cada vez que desee usar el bean RestTemplate para llamar a un servicio, solo necesita conéctelo automáticamente a la clase que lo usa.

El uso de la clase RestTemplate con balanceador de carga se comporta prácticamente como una clase Spring RestTemplate estándar, excepto por una pequeña diferencia en cómo se define la URL para el servicio de destino. En lugar de utilizar la ubicación física del servicio en la llamada RestTemplate, creará la URL de destino utilizando el ID de servicio de Eureka del servicio al que desea llamar.

Veamos esta diferencia mirando el siguiente código.

/*Package and import definitions left off for conciseness*/

@Component

public class OrganizationRestTemplateClient {

  @Autowired
  RestTemplate restTemplate;

  public Organization getOrganization(String organizationId){
    ResponseEntity<Organization> restExchange =
    restTemplate.exchange("http://organizationservice/v1/organizations/{organizationId}",
                   HttpMethod.GET, null, Organization.class, organizationId);
    return restExchange.getBody();
  }
}

Este código debería parecerse un poco al ejemplo anterior, excepto por dos diferencias clave. Primero, Spring Cloud DiscoveryClient no está a la vista. En segundo lugar, la URL que se utiliza en la llamada restTemplate.exchange() debería parecerte extraña:

restTemplate.exchange("http://organizationservice/v1/organizations/{organizationId}",
                   HttpMethod.GET, null, Organization.class, organizationId);

El nombre del servidor en la URL coincide con el ID de la aplicación de la clave de servicio de la organización con la que registró el servicio de la organización en Eureka:

http://{identificación de la aplicación}/v1/organizaciones/{identificación de la organización}

El RestTemplate habilitado para Ribbon analizará la URL que se le pasó y usará lo que se le pase como el nombre del servidor como clave para consultar a Ribbon por una instancia de un servicio. La ubicación real del servicio y el puerto están completamente abstraídos del desarrollador.

Además, al usar la clase RestTemplate, Ribbon equilibrará la carga por turnos de todas las solicitudes entre todas las instancias de servicio.


sábado, 26 de febrero de 2022

Descubrir un servicio publicado con Eureka con Spring boot, parte 2


Seguimos con el post anterior. Pero para no quedar tan colgado retomemos este código : 

/*Packages and imports removed for conciseness*/

@Component

public class OrganizationDiscoveryClient {


  @Autowired

   private DiscoveryClient discoveryClient;


   public Organization getOrganization(String organizationId) {

     RestTemplate restTemplate = new RestTemplate();

     List<ServiceInstance> instances = discoveryClient.getInstances("organizationservice");

     if (instances.size()==0) return null;

        String serviceUri = String.format("%s/v1/organizations/%s", instances.get(0).getUri().toString(),

            organizationId);

        ResponseEntity< Organization > restExchange = restTemplate.exchange(serviceUri, 

                                                                                                     HttpMethod.GET,

                                                                                                     null, Organization.class, organizationId);

      return restExchange.getBody();

   }

}

Solo debemos usar Discovery-Client directamente cuando el servicio necesita consultar Ribbon para comprender qué servicios e instancias de servicio están registrados con él. Hay varios problemas con este código, incluidos los siguientes:

No se está aprovechando el balanceador de carga del lado del cliente de Ribbon: al llamar a Discovery-Client directamente, obtenemos una lista de servicios, pero tambien responsabilidad elegir qué instancias de servicio a invocar.

Se está haciendo demasiado trabajo: en este momento, debe crear la URL que se utilizará para llamar a su servicio. Es algo pequeño, pero cada pieza de código que puede evitar escribir es una pieza menos de código que tiene que depurar.

Es posible que los desarrolladores observadores de Spring hayan notado que está instanciando directamente la clase RestTemplate en el código. Esto es la antítesis de las invocaciones normales de Spring REST, ya que normalmente Spring Framework inyectaría RestTemplate en la clase que lo usa a través de la anotación @Autowired.

Ha creado una instancia de la clase RestTemplate porque una vez que haya habilitado Spring DiscoveryClient en la clase de aplicación a través de la anotación @EnableDiscovery-Client, todas las RestTemplates administradas por Spring Framework tendrán un interceptor habilitado para Ribbon. Al instanciar directamente la clase RestTemplate le permite evitar este comportamiento.

En resumen, existen mejores mecanismos para llamar a un servicio respaldado por Ribbon.

jueves, 24 de febrero de 2022

Libros Gratuitos de Java code Geeks

 

Download IT Guides!

 

JPA Minibook

The basic Java framework to access the database is JDBC. Unfortunately, with JDBC, a lot of hand work is needed to convert a database query result into Java classes. Other disadvantages...

 
 

Microservices for Java Developers

Microservices are a software development technique – a variant of the service-oriented architecture (SOA) structural style – that arranges an application as a collection of loosely...

 
 

JMeter Tutorial

JMeter is an application that offers several possibilities to configure and execute load, performance and stress tests using different technologies and protocols. It allows simulating...

 
 

Java Design Patterns

A design pattern is a general reusable solution to a commonly occurring problem within a given context in software design. A design pattern is not a finished design that can be...

 

martes, 22 de febrero de 2022

Curso de introducción a Linux en español

 Me llego este mail y queria compartirlo : 

 
Courses   /   Programs   /   My Account
 
Introduccion a Linux.
 
 
Linux es el sistema operativo del 100% de las supercomputadoras del mundo, también de la mayoría de los servidores que alimentan el Internet y de los que realizan operaciones financieras en todo el mundo, siendo además la base de los dos mil millones de dispositivos Android.

Este curso te ayudará a desarrollar un buen conocimiento práctico de Linux, utilizando tanto la interfaz gráfica como la línea de comandos y conociendo las principales distribuciones de este sistema operativo de software libre.

Este curso es ofrecido por The Linux Foundation y la Universitat Politècnica de València. Únete a los más de 1,000,000 de estudiantes que ya están inscritos en la versión de este curso en inglés y comienza tu viaje por Linux.

Lo que aprenderás:
Obtendrás un buen conocimiento práctico de Linux
Aprenderás cómo navegar por las principales distribuciones de Linux
Conocerás la configuración del sistema y de la interfaz gráfica de Linux
Aprenderás las operaciones básicas de la línea de comandos
Identificarás las aplicaciones comunes de Linux
 
Explora el curso
 
 

edX is the trusted platform for education and learning.

Save 10% on select programs! Click here

edX for Business — eLearning Solutions for Your Company

© 2022 edX Inc. All rights reserved.

Update Your Preferences / Unsubscribe

141 Portland St. 9th Floor, Cambridge, MA 02139

lunes, 21 de febrero de 2022

Descubrir un servicio publicado con Eureka con Spring boot


Ya tenemos el servicio registrado en Eureka. Ahora necesitamos llamarlo sin tener conocimiento directo de la ubicación. De tal manera que este ultimo buscará la ubicación física mediante Eureka.

Para nuestros propósitos, vamos a ver tres bibliotecas de clientes de Spring/Netflix diferentes en las que un consumidor de servicios puede interactuar con Ribbon. Estas bibliotecas pasarán del nivel más bajo de abstracción para interactuar con Ribbon al más alto.

Las bibliotecas que exploraremos incluyen

  •  Spring Discovery 
  •  Spring Discovery  con RestTemplate habilitado 
  •  Netflix Feign

Spring Discovery Client ofrece el nivel más bajo de acceso a Ribbon y los servicios registrados en él. Usando Discovery Client, puede consultar todos los servicios registrados con el cliente y sus URL correspondientes.

Veamos un ejemplo simple del uso de DiscoveryClient para recuperar una de las direcciones URL de un servicio y luego llamaremos al servicio mediante una clase RestTemplate estándar. Para comenzar a usar DiscoveryClient, primero debe anotar la clase Application.java con la anotación @EnableDiscoveryClient:

@SpringBootApplication

@EnableDiscoveryClient

public class Application {
   public static void main(String[] args) {
     SpringApplication.run(Application.class, args);
  }
}

Veamos un ejemplo de un descubrimiento de un cliente : 

/*Packages and imports removed for conciseness*/
@Component
public class OrganizationDiscoveryClient {

  @Autowired
   private DiscoveryClient discoveryClient;

   public Organization getOrganization(String organizationId) {
     RestTemplate restTemplate = new RestTemplate();
     List<ServiceInstance> instances = discoveryClient.getInstances("organizationservice");
     if (instances.size()==0) return null;
        String serviceUri = String.format("%s/v1/organizations/%s", instances.get(0).getUri().toString(),
            organizationId);
        ResponseEntity< Organization > restExchange = restTemplate.exchange(serviceUri, 
                                                                                                     HttpMethod.GET,
                                                                                                     null, Organization.class, organizationId);
      return restExchange.getBody();
   }
}

DiscoveryClient es la clase que usamos para interactuar con Ribbon. Para recuperar todas las instancias de los servicios de la organización registrados con Eureka, se puede usar el método getInstances(), pasando la clave del servicio que está buscando, para recuperar una lista de objetos ServiceInstance.

La clase ServiceInstance se utiliza para contener información sobre una instancia específica de un servicio, incluido su nombre de host, puerto y URI.

Tomamos la primera clase ServiceInstance de su lista para crear una URL de destino que luego se puede usar para llamar a su servicio. Una vez que tenga una URL de destino, puede usar un Spring RestTemplate estándar para llamar al servicio y recuperar datos.

En post posteriores veremos otras opciones. 

viernes, 18 de febrero de 2022

El poder de las type class y los implícitos en Scala


El poder de las type class y los implícitos radica en la capacidad del compilador para combinar definiciones implícitas al buscar instancias candidatas.

Anteriormente insinuamos que todas las instancias de type class son valores implícitos. Esto fue una simplificación. De hecho, podemos definir instancias de dos maneras:

  1. definiendo instancias concretas como valores implícitos del tipo requerido;
  2. mediante la definición de métodos implícitos para construir instancias a partir de otras instancias de clases de tipo.

¿Por qué construiríamos instancias a partir de otras instancias? Como ejemplo motivacional, considere definir un JsonWriter para Option. necesitaríamos un JsonWriter[Option[A]] para cada A que nos interesa en nuestra aplicación. Podríamos intentar resolver el problema por fuerza bruta creando una biblioteca de valores implícitos:

implicit val optionIntWriter: JsonWriter[Option[Int]] =

???

implicit val optionPersonWriter: JsonWriter[Option[Person]] =

???

// and so on...


Sin embargo, este enfoque claramente no escala. Terminamos requiriendo dos valores implícitos para cada tipo A en nuestra aplicación: uno para A y otro para Opción[A].

Afortunadamente, podemos abstraer el código para manejar la Opción[A] en un constructor común basado en la instancia de A:

• si la opción es Some(aValue), escriba aValue usando el escritor para A;

• si la opción es None, devuelve JsNull.

Aquí está el mismo código escrito como una definición implícita:

implicit def optionWriter[A] (implicit writer: JsonWriter[A]): JsonWriter[Option[A]] =

  new JsonWriter[Option[A]] {

    def write(option: Option[A]): Json =

       option match {

           case Some(aValue) => writer.write(aValue)

           case None => JsNull

      }

  }

Este método construye un JsonWriter para Option[A] basándose en un parámetro implícito para completar la funcionalidad específica de A. Cuando el compilador ve una expresión como esta:

  Json.toJson(Option("A string"))

busca un JsonWriter[Option[String]] implícito. Encuentra el método implícito para JsonWriter[Option[A]]:

  Json.toJson(Option("A string"))(optionWriter[String])

y busca recursivamente un JsonWriter[String] para usarlo como parámetro para optionWriter:

  Json.toJson(Option("A string"))(optionWriter(stringWriter))

De esta forma, la resolución implícita se convierte en una búsqueda a través del espacio de posibles combinaciones de definiciones implícitas, para encontrar una combinación que cree una instancia de clase de tipo del tipo general correcto.

Trabajar con implícitos en Scala


Trabajar con type class en Scala significa trabajar con valores implícitos y parámetros implícitos. Hay algunas reglas que necesitamos saber para hacer esto de manera efectiva.

Implícitos de empaquetado : En una peculiaridad curiosa del lenguaje, cualquier definición marcada como implícita en Scala debe colocarse dentro de un objeto o trait en lugar de en el nivel superior. Colocar instancias en un objeto complementario a la clase de tipo tiene un significado especial en Scala porque juega con algo llamado alcance implícito.

Ámbito implícito : El compilador busca instancias de clase de tipo candidato por tipo. Los lugares donde el compilador busca instancias candidatas se conocen como ámbito implícito. El alcance implícito se aplica en el sitio de la llamada; ese es el punto donde llamamos a un método con un parámetro implícito. El alcance implícito que consta aproximadamente de:

• definiciones locales o heredadas;

• definiciones importadas;

• definiciones en el objeto complementario de la clase de tipo o el tipo de parámetro (en este caso, JsonWriter o String).

Las definiciones solo se incluyen en el ámbito implícito si están etiquetadas con la palabra clave implícita. Además, si el compilador ve múltiples definiciones candidatas, falla con un error de valores implícitos ambiguos:

implicit val writer1: JsonWriter[String] = JsonWriterInstances.stringWriter

implicit val writer2: JsonWriter[String] = JsonWriterInstances.stringWriter

Json.toJson("A string")

// error: ambiguous implicit values:

//  both value writer1 in object App0 of type => repl.Session.App0. JsonWriter[String]

//  and value writer2 in object App0 of type => repl.Session.App0. JsonWriter[String]

// match expected type repl.Session.App0.JsonWriter[String]

// Json.toJson("A string")

// ^^^^^^^^^^^^^^^^^^^^^^^

Las reglas precisas de la resolución implícita son más complejas que esto, pero la complejidad es en gran medida irrelevante para el uso diario. Para nuestros propósitos, podemos empaquetar instancias de clases de tipo en aproximadamente cuatro formas:

1. colocándolos en un objeto;

2. colocándolos en un trait;

3. colocándolos en el objeto compañero de la clase de tipos;

4. colocándolos en el objeto complementario del tipo de parámetro.

Con la opción 1 traemos las instancias al alcance importándolas. Con la opción 2 los traemos al alcance con la herencia. Con las opciones 3 y 4, las instancias siempre están en un alcance implícito, independientemente de dónde intentemos usarlas.

Es convencional colocar instancias de clase de tipo en un objeto complementario (opción 3 y 4 anteriores) si solo hay una implementación sensata, o al menos una implementación ampliamente aceptada como predeterminada. Esto hace que las instancias de clase de tipo sean más fáciles de usar, ya que no se requiere importarlas para incluirlas en el ámbito implícito.

jueves, 17 de febrero de 2022

Type class en Scala parte 2


Seguimos con Type class en Scala

Un uso de clase de tipo es cualquier funcionalidad que requiere una instancia de clase de tipo para funcionar. En Scala esto significa cualquier método que acepte instancias de la clase de tipo como parámetros implícitos.

La forma más sencilla de crear una interfaz que utilice una clase de tipo es colocar métodos en un objeto único:

object Json {

  def toJson[A](value: A)(implicit w: JsonWriter[A]): Json =

    w.write(value)

}

Para usar este objeto, importamos cualquier instancia de clase de tipo que nos interese y llamamos al método relevante:

import JsonWriterInstances._
Json.toJson(Person("Dave", "dave@example.com"))
// res1: Json = JsObject(
// Map("name" -> JsString("Dave"), "email" -> JsString("dave@example.com"))
// )

El compilador detecta que hemos llamado al método toJson sin proporcionar los parámetros implícitos. Intenta arreglar esto buscando instancias de clase de tipo de los tipos relevantes e insertándolos en el sitio de la llamada:

Json.toJson(Person("Dave", "dave@example.com"))(personWriter)

Alternativamente, podemos usar métodos de extensión para extender los tipos existentes con métodos de interfaz. 

object JsonSyntax {
  implicit class JsonWriterOps[A](value: A) {
    def toJson(implicit w: JsonWriter[A]): Json =
      w.write(value)
  }
}

Usamos la sintaxis de la interfaz importándola junto con las instancias para los tipos que necesitamos:

import JsonWriterInstances._

import JsonSyntax._

Person("Dave", "dave@example.com").toJson

// res3: Json = JsObject(

// Map("name" -> JsString("Dave"), "email" -> JsString("dave@example.com"))

// )

Nuevamente, el compilador busca candidatos para los parámetros implícitos y los completa por nosotros:

Person("Dave", "dave@example.com").toJson(personWriter)

La biblioteca estándar de Scala proporciona una interfaz de clase de tipo genérico llamada implicitly. Su definición es muy sencilla:

def implicitly[A](implicit value: A): A = value

Podemos usar implicitly para invocar cualquier valor del alcance implícito. Proporcionamos el tipo que queremos e implícitamente hace el resto:

import JsonWriterInstances._

implicitly[JsonWriter[String]]
// res5: JsonWriter[String] = repl.

Podemos usar implicitly para invocar cualquier valor del alcance implícito. Proporcionamos el tipo que queremos e implícitamente hace el resto:

import JsonWriterInstances._
implicitly[JsonWriter[String]]
// res5: JsonWriter[String] = repl.

Podemos insertar una llamada a implícitamente dentro del flujo general de nuestro código para garantizar que el compilador pueda encontrar una instancia de una clase de tipo y asegurarse de que no haya errores implícitos ambiguos.

domingo, 13 de febrero de 2022

Haciendo test con Spock


Si deseas hacer tus test más legibles y acordes a la metodologia BDD, spock es para vos. 

Spock es un framework de test y especificación para aplicaciones Java y Groovy. Lo que lo hace destacar entre la multitud de framework de test es su hermoso y altamente expresivo lenguaje de especificaciones. Gracias a que corre sobre JUnit, Spock es compatible con la mayoría de los IDE, herramientas de compilación y servidores de integración continua. Spock está inspirado en JUnit, jMock, RSpec, Groovy, Scala, Vulcans y otros frameworks y lenguajes.

La idea de spock es que hay diferentes bloques donde uno puede definir el setup del test, el estimulo y la respuesta esperada : 


Bueno, nada mejor que un ejemplo para mostrar la propuesta de este framework: 

def "two plus two should equal four"() {

    given:

        int left = 2

        int right = 2

    when:

        int result = left + right

    then:

        result == 4

}

def "Should be able to remove from list"() {

    given:

        def list = [1, 2, 3, 4]

    when:

        list.remove(0)

    then:

        list == [2, 3, 4]

}


Dejo link: https://spockframework.org/

sábado, 12 de febrero de 2022

Typescript handbook


Si empezas con typescript un libro super recomendado es el handbook que se puede acceder de forma gratuita. 

En la introducción nos da una perspectiva del libro para diferentes desarrolladores:

También se puede bajar como Pdf : https://www.typescriptlang.org/assets/typescript-handbook.pdf

Dejo link: https://www.typescriptlang.org/docs/handbook/intro.html

viernes, 11 de febrero de 2022

Haciendo test de integración con Testcontainers


Si tenes que hacer un test de integración por ejemplo con una base de datos mongodb, o un ldap o no sé...

Lo mejor que podes hacer es que los test no se conecten al software externo, porque si esta caído o hay un problema de red, estos test van a fallar. Y eso esta mal, solo deben fallar si un desarrollador, cambia algo que no debía. 

Por ende, una buena idea es embeber el software externo en nuestros test. De este modo, el software externo se va a levantar con los test y deja de ser algo externo para ser una pieza de nuestros test, que podemos controlar. 

Claro lo malo es que tenemos que trabajar muchísimo para embeber software externo. Pero pero esto no es así, tenemos testconteiners. Donde podemos encontrar de todo para probar. 

Veamos un ejemplo de un test que usa un contenedor de la base de datos Redis : 

@Testcontainers
public class RedisBackedCacheIntTest {

    private RedisBackedCache underTest;

    // container {
    @Container
    public GenericContainer redis = new GenericContainer(DockerImageName.parse("redis:5.0.3-alpine"))
                                            .withExposedPorts(6379);
    // }


    @BeforeEach
    public void setUp() {
        String address = redis.getHost();
        Integer port = redis.getFirstMappedPort();

        // Now we have an address and port for Redis, no matter where it is running
        underTest = new RedisBackedCache(address, port);
    }

    @Test
    public void testSimplePutAndGet() {
        underTest.put("test", "example");

        String retrieved = underTest.get("test");
        assertEquals("example", retrieved);
    }
}

Este ejemplo es con JUnit 5 pero se puede utilizar con JUnit 4 y Spock. Y si ven la pagina van a encontrar un monton de tecnologías contenizadas para probar desde kafka, pulsar, oracle, mariadb, CosmosDB, Solr, y mucho más.

Sin más dejo link: https://www.testcontainers.org/

jueves, 10 de febrero de 2022

Type class en Scala


Type class es un patrón de programación que se origina en Haskell. Nos permiten ampliar las bibliotecas existentes con nuevas funciones, sin utilizar la herencia tradicional y sin alterar el código fuente de la biblioteca original.

Type class es una especie de interfaz que define algún tipo de comportamiento. Si un tipo es miembro de una clase de tipos, significa que ese tipo soporta e implementa el comportamiento que define la clase de tipos. La gente que viene de lenguajes orientados a objetos es propensa a confundir las clases de tipos porque piensan que son como las clases en los lenguajes orientados a objetos. Bien, pues no lo son. Una aproximación más adecuada sería pensar que son como las interfaces de Java, o los protocolos de Objective-C, pero mejor.

Veamos un ejemplo: 

ghci> :t (==)

(==) :: (Eq a) => a -> a -> Bool

Interesante. Aquí vemos algo nuevo, el símbolo =>. Cualquier cosa antes del símbolo => es una restricción de clase. Podemos leer la declaración de tipo anterior como: la función de igualdad toma dos parámetros que son del mismo tipo y devuelve un Bool. El tipo de estos dos parámetros debe ser miembro de la clase Eq (esto es la restricción de clase).

El tipo de clase Eq proporciona una interfaz para las comparaciones de igualdad. Cualquier tipo que tenga sentido comparar dos valores de ese tipo por igualdad debe ser miembro de la clase Eq. Todos los tipos estándar de Haskell excepto el tipo IO (un tipo para manejar la entrada/salida) y las funciones forman parte de la clase Eq.

Hay tres componentes importantes para implementar este patrón en Scala. Type class en Scala se implementan usando valores y parámetros implícitos y, opcionalmente, usando clases implícitas. Las construcciones del lenguaje Scala corresponden a los componentes de los type class de la siguiente manera:

  • traits: type classes;
  • implicit values: instancia del type class;
  • implicit parameters: donde se usa el type class use
  • implicit classes: es opcional, y facilita el uso de type class
Veamos estos puntos en más detalle : 

traits : Una clase de tipo es una interfaz o API que representa alguna funcionalidad que queremos implementar. En Scala, una clase de tipo está representada por un rasgo o traits con al menos un parámetro de tipo. Por ejemplo, podemos representar el comportamiento genérico de "serializar a JSON" de la siguiente manera:

// Define a very simple JSON AST
sealed trait Json
  final case class JsObject(get: Map[String, Json]) extends Json
  final case class JsString(get: String) extends Json
  final case class JsNumber(get: Double) extends Json
  final case object JsNull extends Json

// The "serialize to JSON" behaviour is encoded in this trait
trait JsonWriter[A] {
  def write(value: A): Json
}

JsonWriter es nuestra clase de tipo en este ejemplo, con Json y sus subtipos proporcionando código de soporte. Cuando lleguemos a implementar instancias de JsonWriter, el parámetro de tipo A será el tipo concreto de datos que estamos escribiendo.

Las instancias de una clase de tipo proporcionan implementaciones de la clase de tipo para tipos específicos que nos interesan, que pueden incluir tipos de la biblioteca estándar de Scala y tipos de nuestro modelo de dominio.

En Scala, definimos instancias creando implementaciones concretas de la clase de tipo y etiquetándolas con la palabra clave implícita:

final case class Person(name: String, email: String)
  object JsonWriterInstances {
    implicit val stringWriter: JsonWriter[String] =
      new JsonWriter[String] {
        def write(value: String): Json =
          JsString(value)
      }

implicit val personWriter: JsonWriter[Person] =
  new JsonWriter[Person] {
    def write(value: Person): Json =
      JsObject(Map(
        "name" -> JsString(value.name),
        "email" -> JsString(value.email)
      ))
  }

//etc 
}

Puff me quedo relargo el post, seguimos con implicit parameters y implicit classes en el próximo post.