diff --git a/.overcommit.yml b/.overcommit.yml
new file mode 100644
index 0000000..7438e82
--- /dev/null
+++ b/.overcommit.yml
@@ -0,0 +1,29 @@
+# Use this file to configure the Overcommit hooks you wish to use. This will
+# extend the default configuration defined in:
+# https://github.com/sds/overcommit/blob/master/config/default.yml
+#
+# At the topmost level of this YAML file is a key representing type of hook
+# being run (e.g. pre-commit, commit-msg, etc.). Within each type you can
+# customize each hook, such as whether to only run it on certain files (via
+# `include`), whether to only display output if it fails (via `quiet`), etc.
+#
+# For a complete list of hooks, see:
+# https://github.com/sds/overcommit/tree/master/lib/overcommit/hook
+#
+# For a complete list of options that you can use to customize hooks, see:
+# https://github.com/sds/overcommit#configuration
+#
+# Uncomment the following lines to make the configuration take effect.
+
+PreCommit:
+ TrailingWhitespace:
+ enabled: true
+ exclude:
+ - '**/coverage/*html' # Ignore trailing whitespace in generated files
+
+PostCheckout:
+ ALL: # Special hook name that customizes all hooks of this type
+ quiet: true # Change all post-checkout hooks to only display output on failure
+
+ IndexTags:
+ enabled: true # Generate a tags file with `ctags` each time HEAD changes
diff --git a/.rubocop.yml b/.rubocop.yml
new file mode 100644
index 0000000..7ea07ce
--- /dev/null
+++ b/.rubocop.yml
@@ -0,0 +1,3 @@
+AllCops:
+ Exclude:
+ - Gemfile.lock
diff --git a/000-cover.es.md b/000-cover.es.md
new file mode 100644
index 0000000..dcb06a3
--- /dev/null
+++ b/000-cover.es.md
@@ -0,0 +1,9 @@
+# Creando Aplicaciones Distribuidas con Rails
+
+## Usando Protocol Buffers, NATS y RabbitMQ
+
+### Kevin Watson
+
+Traducción al Español: Luilver Garcés Briñas
+
+[Siguiente >>](001-preface.es.md)
diff --git a/001-preface.es.md b/001-preface.es.md
new file mode 100644
index 0000000..e7a08f9
--- /dev/null
+++ b/001-preface.es.md
@@ -0,0 +1,60 @@
+## Prefacio a la edición en Español
+
+Bienvenido a *Creando Aplicaciones Distribuidas con Rails*.
+
+Ruby on Rails es una plataforma construida sobre el lenguaje de programación
+Ruby. La plataforma Rails provee las herramientas necesarias para construir
+aplicaciones con bases de datos y ha logrado una popularidad generalizada,
+actualmente muchos sitios populares corren sobre Rails, entre los que se
+incluyen Shopify, Basecamp y Github.
+
+Una arquitectura de aplicaciones distribuidas define modulos especializados o
+componentes de un sistema en el cual, como un todo, proveen la funcionalidad que
+los usuarios requiren. Las aplicaciones distribuidas pueden ser configuradas
+para escalar tanto hacia arriba como hacia abajo según sea necesario,
+especificamente por aquellos módulos que requieran un poder de cómputo adicional
+. Por ejemplo el módulo que muestra el formulario para el inicio de sesión puede
+no requerir mucha potencia de cómputo pero el que optimiza las fotos que suben
+los clientes puede necesitar ponerlas a escala cada vez que un usuario añade un
+conjunto nuevo de imágenes.
+
+Este libro te adentrará a través del proceso de crear aplicaciones distribuidas
+usando Ruby on Rails. Discutiremos aplicaciones monolíticas las cuales serán a
+su vez divididas en unidades más pequeñas (microservicios) y describiremos
+algunas formas de compartir datos entre estos microservicios. Usaremos un
+un pequeño grupo de gemas de Ruby que han sido generosamente liberadas por
+[MX][] para su uso por parte de la comunidad de código abierto.
+
+MX es una compañía de servicios financieros basada en Utah. Sus miembros hemos
+desarrollado una plataforma, desde sus inicios, distribuida y heterogénea que
+procesa y analiza miles de millones de transacciones financieras cada mes.
+Las contribuciones de MX a los estándares abiertos Protobuf, RabbitMQ y NATS
+incluyen, pero no se limitan a, las siguiente gemas: `active_remote`,
+`protobuf-nats`, `action_subscriber`, `active_publisher` and
+`protobuf-activerecord`. Discutiremos cada una de ellas con detalles a lo largo
+de este libro.
+
+Ruby por su parte continúa evolucionando y ganando en popularidad. Las
+contribuciones de MX a la comunidad de código abierto ayudan a asegurar que Ruby
+coninúe siendo una opción viable para diseñar y desplegar modernos sistemas
+distribuidos.
+
+Debido a que la plataforma de MX está construida sobre estándares abiertos (Ej:
+ Protocol Buffers, RabbitMQ y NATS) es agnóstica a los lenguajes de programación
+ que sean empleados para crear nuevos servicios sobre ella. Siempre y cuando un
+ servicio se comunique usando mensajes Protobuff y se conecte a NATS o a
+ RabbitMQ podrá responder a mensajes desde cualquier aplicación escrita en
+cualquier lenguaje de programación soportado.
+
+Inlcuso si lenguaje no fuera Ruby, ojalá este libro brinde una visión general de
+cómo diseñar y construir servicios distribuidos.
+
+Hemos querido llevar al público hispanoparlante esta obra de valor incalculable
+ya que casi toda la información relativa al desarrollo de microservicios en la
+nube se genera en países en los que no se habla español. Esto provoca que sea
+difícil para nosotros acceder en nuestro idioma nativo a información actualizada
+y de calidad sobre estos temas. (N. del T.)
+
+[Siguiente >>](002-who-is-this-book-for.es.md)
+
+[MX]: https://mx.com
diff --git a/002-who-is-this-book-for.es.md b/002-who-is-this-book-for.es.md
new file mode 100644
index 0000000..1257113
--- /dev/null
+++ b/002-who-is-this-book-for.es.md
@@ -0,0 +1,10 @@
+## ¿Para quién es este libro?
+
+Este libro es para cualquiera que esté interesado en desarrollar una plataforma
+distribuida compuesta por múltiples aplicaciones, también conocido como una
+plataforma de microservicios. Aunque todos los ejemplos a continuación están
+escritos en Ruby, usando la plataforma Ruby on Rails, un desarrollador de
+software puede adentrarse y traducir la funcionalidad propuesta usando el
+lenguaje de programación y plataforma de su preferencia.
+
+[Siguiente >>](003-whats-in-this-book.es.md)
diff --git a/003-whats-in-this-book.es.md b/003-whats-in-this-book.es.md
new file mode 100644
index 0000000..0e03f9d
--- /dev/null
+++ b/003-whats-in-this-book.es.md
@@ -0,0 +1,24 @@
+## ¿Qué contiene este Libro?
+
+Este libro está compuesto por muchas secciones a lo largo de las cuales
+discutiremos diferentes patrones de arquitectura para desarollar systemas
+distribuidos o no tan distribuidos. También discutiremos varios métodos de
+comunicación de datos entre las diferentes aplicaciones.
+
+Discutiremos además la plataforma Ruby on Rails y los ladrillos necesarios, en
+todo proceso constructivo, para proveer acceso a los datos que nuestra
+aplicación procesará. Analizaremos plataformas de comunicación via mensajes,
+entidades de modelación de datos y sus relaciones en un entorno distribuido.
+
+Daremos los pasos necesarios para armar un nuevo entorno que consista en un
+servidor NATS y dos aplicaciones Rails, una con un modelo soportado por una
+base de datos y otra con un modelo que accede de manera remota a los datos
+de la primera aplicación.
+
+Debatiremos sobre mensajes orientados a eventos y cuándo es apropiado; mientras
+construimos dos aplicaciones que se comunican usando RabbitMQ.
+
+Por último construiremos una plataforma que use ambos patrones de arquitectura:
+síncrono y orientado a eventos, para compartir los datos entre los servicios.
+
+[Siguiente >>](004-what-you-need.es.md)
diff --git a/003-whats-in-this-book.md b/003-whats-in-this-book.md
index 1aab40e..df63a18 100644
--- a/003-whats-in-this-book.md
+++ b/003-whats-in-this-book.md
@@ -1,6 +1,6 @@
## What's in This Book?
-This book has several sections. We'll discuss several architectural patterns for building distributed or not-so-distributed systems. We'll discuss various methods of moving data between applications.
+This book has several sections. We'll discuss several architectural patterns for building distributed or not-so-distributed systems. We'll discuss various methods of moving data between applications.
We'll discuss the Ruby on Rails framework, and the building blocks that provide access to the data your application will process. We'll discuss messaging platforms. We'll discuss modeling data entities and their relationships in a distributed environment.
diff --git a/004-what-you-need.es.md b/004-what-you-need.es.md
new file mode 100644
index 0000000..c1ecca3
--- /dev/null
+++ b/004-what-you-need.es.md
@@ -0,0 +1,18 @@
+## Lo que necesitaremos
+
+Necesitaremos una computadora que pueda correr Docker.[^1] Necesitaremos, de
+manera opcional, connección a Internet si deseamos descargar el código de
+ejemplo.
+
+Todas las herramientas presentadas en este libro pueden ser utilizadas de
+manera gratuita. El software que usaremos es tanto gratuito como de código
+abierto y puede ser descargado, instalado y usado sin costo adicional.
+
+Los proyectos presentados en este libro fueron desarrollados en una MacBook con
+un entorno de contenedores usando Docker Desktop. Los comandos debieran funionar
+de igual manera si se utiliza Docker para Linux, Windows o macOS.
+
+[^1]: En el momento de esta edición Docker Desktop y Colima fueron probados con
+éxito.
+
+[Siguiente >>](005-online-resources.es.md)
diff --git a/005-online-resources.es.md b/005-online-resources.es.md
new file mode 100644
index 0000000..4830f95
--- /dev/null
+++ b/005-online-resources.es.md
@@ -0,0 +1,47 @@
+## Recursos en línea
+
+El código fuente al que se hace referencia en este libro está disponible para
+su descarga en [Github][]. Podemos descargar y ejecutar el código de ejemplo o
+construir las mismas aplicaciones desde cero siguiendo los capítulos 9, 12 y 13.
+
+Adicionalmente al código de ejemplo en Github en este libro se referencian los
+siguientes sitios web:
+
+#### Active Publisher y Action Subscriber
+
+* https://github.com/mxenabled/action_subscriber
+* https://github.com/mxenabled/active_publisher
+
+#### Active Remote
+
+* https://github.com/liveh2o/active_remote
+* https://github.com/liveh2o/protobuf-activerecord
+
+#### Docker
+
+* https://docker.com
+* https://docs.docker.com/compose
+
+#### NATS
+
+* https://nats.io
+
+#### Protocol Buffers (Protobuf)
+
+* https://github.com/abrandoned/protobuf-nats
+* https://github.com/ruby-protobuf
+* https://github.com/ruby-protobuf/protobuf/wiki/Serialization
+
+#### RabbitMQ
+
+* https://www.rabbitmq.com
+
+#### Ruby y Ruby on Rails
+
+* https://www.ruby-lang.org
+* https://rubygems.org
+* https://rubyonrails.org
+
+[Siguiente >>](006-acknowledgements.es.md)
+
+[GitHub]: https://github.com/kevinwatson/rails-microservices-sample-code
diff --git a/006-acknowledgements.es.md b/006-acknowledgements.es.md
new file mode 100644
index 0000000..44c3097
--- /dev/null
+++ b/006-acknowledgements.es.md
@@ -0,0 +1,19 @@
+## Dedicatoria
+
+Primero me gustaría agradecer a mi esposa, Mónica, por su paciencia y
+comprensión cuando persigo aficiones tales como escribir.
+
+Me gustaría agradecer a mi empleador, [MX][], así como a las muchas personas que
+han construido esta compañía, por su experiencia y sus discímiles contribuciones
+al ecosistema de Ruby on Rails. Ellos han publicado documentación valiosa y
+numerosas gemas de Ruby los cuales permiten desarrollar microservicios en Ruby
+on Rails de manera sencilla hoy.
+
+Agradecimientos especiales a Chris Miller quien ha sido mi mentor en Ruby on
+Rails durante muchos cambios de posición laboral. Me gustaría también agradecer
+a Igor Morgunov, Naoto Suzuki, Luilver Garcés Briñas, Mark Oveson y Sheldon
+Teerlink por sus contribuciones y retroalimentación para con este libro.
+
+[Siguiente >>](007-copyright.es.md)
+
+[MX]: https://mx.com
diff --git a/007-copyright.es.md b/007-copyright.es.md
new file mode 100644
index 0000000..681e0c0
--- /dev/null
+++ b/007-copyright.es.md
@@ -0,0 +1,14 @@
+### Creando Applications Distribuidas con Rails
+
+#### Usando Protocol Buffers, NATS y RabbitMQ
+
+Todos los derechos reservados
+
+Copyright © Kevin J. Watson
+
+La información y técnicas empleadas por el autor en este libro se esperan sean
+confiables. Sin embargo el autor no garantiza la precisión o completitud de los
+mismos y no se hace resposable for errores, omisiones o el resultado que
+se obtenga del uso de dicha información.
+
+[Siguiente >>](008-table-of-contents.es.md)
diff --git a/008-table-of-contents.es.md b/008-table-of-contents.es.md
new file mode 100644
index 0000000..fdf4ed6
--- /dev/null
+++ b/008-table-of-contents.es.md
@@ -0,0 +1,79 @@
+### TABLA DE CONTENIDO
+
+* [Capítulo 1 - Microservicios](020-chapter-01.es.md)
+ * Arquitectura del Servicio
+ * Más acerca de los microservicios
+ * ¿Por qué debemos usar microservicios?
+ * Recursos
+ * Recapitulando
+* [Capítulo 2 - Comunicaciones del Servicio](030-chapter-02.es.md)
+ * Introducción
+ * Protocolos
+ * Serialización de datos
+ * Sistemas de mensajes
+ * Recapitulando
+* [Capítulo 3 - Ruby y Ruby on Rails](040-chapter-03.es.md)
+ * Ruby
+ * Ruby on Rails
+ * Recursos
+ * Recapitulando
+* [Capítulo 4 - Active Record y Active Model](050-chapter-04.es.md)
+ * Introducción
+ * Recursos
+ * Recapitulando
+* [Capítulo 5 - Active Remote](060-chapter-05.es.md)
+ * Introducción
+ * Filosofía
+ * Diseño
+ * Implementación
+ * Recursos
+ * Recapitulando
+* [Capítulo 6 - Sistemas de Mensajes- NATS](070-chapter-06.es.md)
+ * Introducción
+ * Ejecutémoslo
+ * Recursos
+ * Recapitulando
+* [Capítulo 7 - Relaciones de Datos](080-chapter-07.es.md)
+ * Introducción
+ * Llaves Primarias y Foráneas
+ * Llaves Naturales Vs. Sustitutas
+ * Base de Datos Vs. Applicación Generada
+ * ¿Cuándo usar cada una?
+ * Recursos
+ * Recapitulando
+* [Capítulo 8 - Protocol Buffers (Protobuf)](090-chapter-08.es.md)
+ * Introducción
+ * Filosofía
+ * Implementación
+ * Recursos
+ * Recapitulando
+* [Capítulo 9 - El Sandbox de Microservicios de Active Remote ](100-chapter-09.es.md)
+ * Introducción
+ * Instalar Docker
+ * Implementación
+ * Recursos
+ * Recapitulando
+* [Capítulo 10 - Mensajes Orientados a Eventos](110-chapter-10.es.md)
+ * Introducción
+ * Implementación
+ * Recapitulando
+* [Capítulo 11 - Sistemas de Mensajes - Rabbit MQ](120-chapter-11.es.md)
+ * Introducción
+ * Ejecutémoslo
+ * Recursos
+ * Recapitulando
+* [Capítulo 12 - El Sandbox de Mensajes Orientados a Eventos](130-chapter-12.es.md)
+ * Introducción
+ * Lo que vamos a necesitar
+ * Implementación
+ * Recursos
+ * Recapitulando
+* [Capítulo 13 - Active Remote con Sandbox Orientado a Eventos](140-chapter-13.es.md)
+ * Introducción
+ * Lo que vamos a necesitar
+ * Implementación
+ * Recursos
+ * Recapitulando
+* [Capítulo 14 - Sumario](150-chapter-14.es.md)
+
+[Siguiente >>](010-chapter-00.es.md)
diff --git a/010-chapter-00.es.md b/010-chapter-00.es.md
new file mode 100644
index 0000000..731c382
--- /dev/null
+++ b/010-chapter-00.es.md
@@ -0,0 +1,6 @@
+> Nosotros, los que picamos simples piedras, deberíamos estar siempre
+> imaginando catedrales.
+>
+> Andrew Hunt, The Pragmatic Programmer: From Journeyman to Master
+
+[Siguiente >>](020-chapter-01.es.md)
diff --git a/020-chapter-01.es.md b/020-chapter-01.es.md
new file mode 100644
index 0000000..5ba4af7
--- /dev/null
+++ b/020-chapter-01.es.md
@@ -0,0 +1,91 @@
+### Capítulo 1 - Microservicios
+
+> Bean, necesito que seas inteligente. Necesito que pienses en soluciones que
+> no hayamos visto aún. Quisiera que intentaras cosas que nadie haya intentado
+> jamás porque sean absolutamente estúpidas.
+>
+> Orson Scott Card, Ender's Game
+
+## Arquitectura del servicio
+
+Primero algunas definiciones:
+
+* **API:** Interfaz de programación de la aplicación (por sus siglas en Inglés).
+En el contexto de este libro, una aplicación que provee acceso a sus datos. Una
+API provee comunicación aplicación-aplicación.
+* **Función como servicio:** FaaS (por sus siglas en Inglés) es una pequeña
+unidad de código que puede ser publicada y consumida sin siquiera contruir o
+mantener la infraestructura circundante. Es considerada la forma de contruir
+arquitecturas sin servidores (serverless).
+* **Microservicio:** Una aplicación que provee funciones y datos específicos.
+* **Monolito:** Una aplicación sencilla que provee toda, sino una gran parte de,
+las funcionalidades que el producto o la compañía require.
+* **Computación sin servidores:** El proveedor en la nube provee y permite
+administrar de manera dinámica los resursos del servicio en la medida que sean
+necesarios. Pequeñas unidades de código, por ejemplo funciones como servicio,
+son desplegadas en un entorno sin servidores.
+* **Servicio:** Un servicio, definido burdamente, es una aplicación independiente
+que provee algún tipo de funcionalidad. Ejemplos podemos encontrar en un sitio
+web, una API, un servidor de bases de datos, etc.
+
+Cuando estamos diseñando un servicio por primera vez los requerimientos suelen
+ser pequeños. Sin embargo en la medida que el tiempo pasa comenzamos a añadir
+características y la aplicación crece llegando a convertirse en un sistema más
+grande. A este tipo de sistema llamamos Monolito y no hay nada malo en construir
+monolitos siempre y cuando puedan manejar la carga de procesamiento. Los
+monolitos son la arquitectura más simple porque son simples de mantener por
+pequeños equipos, todo el código está en el mismo lugar y la comunicaicón entre
+módulos es instantánea pues no hay sobrecarga por la red.
+
+## Más acerca de los microservicios
+
+Un microservicio es una pequeña aplicación capaz de proveer un conjunto limitado
+de funciones. En la filosofía Unix, según describe Doug McIlroy, un princpio
+fundamental es que cada programa haga una sóla cosa y la haga bien. En términos
+de arquitectura de microservicio nuestro objetivo es construir pequeños programas
+o servicios que provean un conunto específico de funciones. En la medida que
+mayor sea el uso de este conjunto de funciones en la organización mayor será la
+necesidad de escalar esa función en particular para mantenerse alineado con las
+necesidades del negocio.
+
+## ¿Por qué debemos usar microservicios?
+
+Por naturaleza, desarrollar servicios basado en la arquitectura de microservicios
+incurre en una sobrecarga adicional que puede no ser adecuada en los momentos
+iniciales de un proyecto. Una vez la aplicación comienza a ser popular y
+comenzamos a identificar cuellos de botella en el proceso, puede ser tiempo de
+identificar y comenzar a agrupar funcionalidades dentro del mismo servicio.
+
+En la medida que el equipo de desarrollo crece puede dividirse el códico base en
+unidades más pequeñas que pueden ser mantenidas y desplegadas de manera
+independiente. Esto a su vez puede traer otros benificios como cilcos más cortos
+de desarrollo, de pruebas y de lanzamiento del producto.
+
+Si las funciones de nuestra aplicación tienen diferentes tiempos de ejecución o
+actividad es posible que el código necesite ser dividido en microservicios más
+pequeños. Esto nos permitirá conocer los requisitos de nivel de servicio en base
+a lo que cada servicio necesita.
+
+## Recursos
+
+* [Filosofía Unix][]
+* [Microservicios en Inglés][]
+
+## Recapitulando
+
+Hay muchas maneras de proveer la lógica que nuestro negocio requiere. Muchos
+negocios comienzan con pequeñas aplicaciones que luego van creciendo en la
+medida que satisfacen las necesidades propias del modelo de negocios o que sus
+usuarios demandan. La mayoría de las veces las aplicaciones de negocios crecen
+y se convierten en aplicaciones monolíticas. Si bien hay muchas razones para
+mantener el monolito, en la medida que el el negocio y el equipo crecen, podemos
+necesitar dividir el código base en unidades más pequeñas y simples de mantener.
+
+En el próximo capítulo discutiremos cómo se comunican los microservicios.
+Veremos los pros y contras de algunos protocolos y métodos de serialización
+que pueden ser utilizados para hacer persistir o codificar los datos
+
+[Siguiente >>](030-chapter-02.es.md)
+
+[Filosofía Unix]: https://es.wikipedia.org/wiki/Filosof%C3%ADa_de_Unix
+[Microservicios en Inglés]: https://martinfowler.com/articles/microservices.html
diff --git a/030-chapter-02.es.md b/030-chapter-02.es.md
new file mode 100644
index 0000000..a6eff2a
--- /dev/null
+++ b/030-chapter-02.es.md
@@ -0,0 +1,215 @@
+### Capítulo 2 - Comunicaciones del servicio
+
+## Introducción
+
+En la medida que la infraestructura del servicio crece necesitamos buscar un
+protocolo de comunicaicón que tenga un buen equilibrio entre desarrollo,
+mantenimiento, velocidad y que sea capaz de adaptarse con facilidad a nuevos
+cambios.
+
+## Protocolos
+
+Varios protocolos pueden ser utilizados para transportar los datos entre los
+servicios. Cada uno tiene sus pros y sus contras, HTTP por ejmplo, es uno de los
+más ampliamente utilizados para páginas webs y APIs tipo REST. HTTP provee
+discímiles características tales como autenticación pero también envía datos de
+encabezado con cada petición. Enviar dichos encabezados con cada petición puede
+causar congestión del tráfico de red cuando estamos diseñando una plataforma que
+para escalar requiere que cada mensaje sea lo más pequeño posible.
+
+_**Table 2-1**_ Protocolos de red
+
+| Protocolo | Ventajas | Desventajas | Ejemplos de uso |
+|---|---|---|---|
+| AMQP | Formato binario que provee mecanismos de cola, enrutamiento y confiabilidada | Sólo soporta formato bienario | Envía y recive mensajes de RabbitMQ |
+| HTTP(S) | Corre encima de TCP, provee métodos de solicitud, autenticación y conexiones persistentes | Algunas características requiren procesamiento adicional y los encabezados también se envían con cada petición | El www, el correo-e, las API tipo REST |
+| NATS | Basado en texto, por lo que los clientes disponen de una gran varieda de lenguajes de programación | Sólo se utiliza para conectarse a un servidor NATS | Publicar hacia o consumir desde un servidor NATS|
+| TCP | Uno de los protocolos de Internet más populares que es usado para estblecer conexiones cliente-servidor; garantizando que los datos sean entregados al cliente y provee mecanismos de detección y reenvío de errores | Más lento que otros protocolos como UDP | SSH, el www, el correo-e |
+| UDP | Su diseño no orientado a conexión permite una mayor velocidad y eficiencia | No provee mecanismos de chequeo de errores ni garantía alguna de que el cliente reciba los datos | Transmisión de video, DNS |
+
+## Serialización de datos
+
+Los datos enviados necesitan ser empaquetados para su entrega. Algunos de los
+mecanismos de empaquetar datos se muestran en la siguiente tabla:
+
+_**Table 2-2**_ Formatos de serialización de datos
+
+| Formato | Texto/Binario | Ventajasn | Desventajas |
+|---|---|---|---|
+| JSON | Texto | Estructurado, inteligible por un humano | Las llaves están presentes en cada objeto lo que los hace mayores en tamaño |
+| Protocol Buffers (Protobuf) | Binario | Compacto | Tanto cliente como servidor deben saber la estructura del mensaje codificado |
+| XML | Texto | Estructurado, inteligible por un humano | Requiere abrir y cerrar etiqutas alrededor de cada campo lo que los hace mayores en tamaño |
+
+### Ejemplos
+
+He aquí algunos ejemplos de datos serializados para cada formato.
+
+#### JSON
+
+JSON (del inglés JavaScript Object Notation) es un formato basado en texto e
+inteligible por un humano, cuya estructura consiste en pares nombre-valor.
+Debido a su estructura simple se ha convertido en una opción popular para
+compartir datos entre servicios.
+
+```json
+[
+ {
+ "id": 1,
+ "first_name": "George",
+ "last_name": "Costanza"
+ },
+ {
+ "id": 2,
+ "first_name": "Elaine",
+ "last_name": "Benes"
+ }
+]
+```
+
+#### Protocol Buffers
+
+Protocol Buffers (Profobuf) es un formato independiente del lenguaje de
+programación que se usa para generar código específico del lenguaje que produce
+mensajes muy pequeños para ser enviados por la red. La principal ventaja es
+la eficiencia de red pero a su vez la principal desventaja es que ambos
+extremos de la comunicación (ciente y servidor) deben estar previamente de
+acuerdo en la estructura del mensaje.
+
+Mientras otros formatos como JSON usan pares nombre-valor para describir cada
+pieza de dato, Protobuf en cambio usa una posición del campo para definir a
+medida que el campo en cuestión es codificado o decodificado en el formato
+binario.
+
+El mensaje Persona (Person en Inglés)
+
+```protobuf
+message Person {
+ int32 id = 1;
+ string first_name = 2;
+ string last_name = 3;
+}
+```
+
+Una lista de gente en un mensaje simple (People es el plural de Person en
+Inglés, N. del T.)
+
+```protobuf
+message PeopleMessageList {
+ repeated PersonMessage records = 1;
+}
+```
+
+##### Implementación en Ruby
+
+La clase Persona
+
+```ruby
+class PersonMessage < ::Protobuf::Message
+ optional :int32, :id, 1
+ optional :string, :first_name, 2
+ optional :string, :last_name, 3
+end
+```
+
+Una clase para representar una lista de gente
+
+```ruby
+class PeopleMessageList < ::Protobuf::Message
+ repeated ::PersonMessage, :records, 1
+end
+```
+
+*Datos serializados*
+
+Los datos a continuación representan una cadena de caracteres en codificación
+binaria.
+
+```console
+# Person 1
+\b\x01\x12\x06George\x1A\bCostanza
+
+# Person 2
+\b\x02\x12\x06Elaine\x1A\x05Benes
+
+# Both
+\n\x14\b\x01\x12\x06George\x1A\bCostanza\n\x11\b\x02\x12\x06Elaine\x1A\x05Benes
+```
+
+#### XML
+
+XML (del Inglés Extensible Markup Language) es un formato basado en texto e
+inteligible por un humano que al igual que JSON define tanto la estructura como
+los datos en el mismo cuerpo del mensaje. XML también es una opción popular para
+el intercambio de datos en Internet y entre sistemas.
+
+```xml
+
+
+ 1
+ George
+ Costanza
+
+
+ 2
+ Elaine
+ Benes
+
+
+```
+
+## Messaging Systems
+
+Hasta ahora hemos descrito protocolos para transferir y empaquetar nuestros
+datos. Ahora analizaremos las arquitecturas establecidas que podemos usar para
+comunicarnos entre sistemas. Los microservicios son aplicaciones pequeñas e
+independientes que pueden comunicarse con otras aplicaciones para realizar algún
+trabajo.
+
+Los servicios pueden llamar directa o indirectamente a otros servicios. Cuando
+un servicio (emisor) llama directamente a otro servicio (receptor), el emisor
+espera que el receptor esté disponible en el recurso que el emisor ya conoce.
+Por ejemplo, si una API aloja un recurso `HTTP` en
+`http://humanresources.internal/employees`, puediéramos escribir un servicio que
+actúe como un cliente que puede llamar a ese recurso. Como respuesta esperaríamos
+recibir una lista de empleados, codificada en algún formato, como `JSON`.
+
+Llamar indirectamente a un servicio significa que hay algún sistema entre los
+dos servicios. Ejemplos de sistemas que actúan como intermediarios incluyen a
+un servidor proxy que puede devolver una copia en caché de los datos o a un
+servidor de cola de mensajes que puede poner en cola las solicitudes y suavizar
+la carga de trabajo del servidor que proporciona los datos.
+
+Los servicios también pueden realizar llamadas de solicitud-respuesta o de
+lanza-y-olvida. El patrón de arquitectura de solicitud-respuesta se utiliza
+cuando el servicio envía una solicitud a otro servicio y espera una respuesta.
+El patrón dispara-y-olvida se utiliza para enviar un mensaje a un servicio
+intermediario que luego notifica a todos los servicios interesados. El remitente
+del mensaje en dispara-y-olvida no espera ninguna respuesta, solo que el
+mensaje sea enviado.
+
+Nuestras necesidades comerciales regirán los patrones de la arquitectura de
+microservicios que finalmente implementaremos. Estos patrones no son mutuamente
+excluyentes por servicio, o dicho de otra manera, se pueden combinar en un solo
+servicio como mostraremos en el capítulo 13.
+
+## Referencias
+
+[Advance Message Queuing Protocol][]
+[Protocol Buffers][]
+[NATS][]
+[JSON][]
+[XML][]
+
+## Recapitulando
+
+En este capítulo discutimos varios protocolos y formatos de mensajes que se
+pueden usar para compartir datos entre servicios. En el próximo capítulo,
+cubriremos el lenguaje Ruby y la plataforma Ruby on Rails.
+
+[Siguiente >>](040-chapter-03.es.md)
+
+[Advance Message Queuing Protocol]: https://www.amqp.org
+[Protocol Buffers]: https://developers.google.com/protocol-buffers
+[NATS]: https://docs.nats.io/nats-protocol/nats-protocol
+[JSON]: https://www.json.org
+[XML]: https://www.w3.org/XML
diff --git a/040-chapter-03.es.md b/040-chapter-03.es.md
new file mode 100644
index 0000000..e967d26
--- /dev/null
+++ b/040-chapter-03.es.md
@@ -0,0 +1,101 @@
+### Capítlo 3 - Ruby y Ruby on Rails
+
+> ¿Qué pasa si cada idea creativa que alguien tiene es adquirida
+> inconscientemente de las experiencias de esa persona en otra realidad? ¿Quizás
+todas las ideas son plagiadas sin que nos demos cuenta, porque nos llegan a
+través de algún desliz de realidad críptico e indemostrable?
+- Elan Mastai, All Our Wrong Todays
+
+## Ruby
+
+El lenguaje de programación Ruby fue diseñado y desarrollado por Yukihiro "Matz"
+Matsumoto a mediado de los '90. El lenguaje estuvo en la sombra hasta que David
+Heinemeier Hansson, en Julio de 2004, publicó la plataforma Ruby on Rails cuya
+popularidad impulsó el desarrollo ulterior de Ruby.
+
+Ruby es un lenguage de alto nivel ya que provee una elevada abstracción de los
+detalles de implementación del hardware del equipo de cómputo. El beneficio
+resultante es que el desarrollador tiene más libertad para escribir código que
+el intérprete de Ruby tiene de procesar, traducir y optimizar para hardware. La
+desventaja es que el desarrollador tiene más libertad para escribir código que
+quizás no sea optimizado para el hardware.
+
+Ruby es, además, un lenguaje interpretdo. Dependiendo de la implementación un
+lenguaje interpretdo corre sobre una máquina virtual la que a su vez corre sobre
+el procesador del equipo de cómputo. En cambio los lenguajes compilados son
+convertidos a código intermedio (por lo general bytecode) y corren directamente
+sobre el procesador. Debido a la capa adicional que presupone la máquina virtual
+los lenguajes interpretdos suelen ser más lentos.
+
+## Ruby on Rails
+
+Por el simple hecho de estar leyendo este libro, lo más probable es que el
+lector esté familiarizado con los beneficios del lenguaje Ruby y la plataforma
+Ruby on Rails. Si no, mi opinión es que el lenguaje Ruby y Ruby on Rails
+brindan las herramientas que un desarrollador necesita para ser altamente
+productivo al crear aplicaciones web. Las nuevas aplicaciones se pueden activar
+en cuestión de segundos. Hay una gran cantidad de bibliotecas disponibles
+(conocidas en el mundo de Ruby como gemas), que se pueden usar para ampliar la
+funcionalidad de su aplicación. Por ejemplo, si necesita ejecutar procesos en
+segundo plano, hay una gema para eso: Sidekiq. Si su aplicación necesita
+administrar dinero y monedas, hay una gema para eso: Money. podría seguir, pero
+se comprende el punto.
+
+Para más información sobre porqué debemos usar Rails, por favor, revice la
+[documentación oficial][].
+
+### Interpretes
+
+Existen algunos intérpretes de Ruby disponibles. Discutiremos un par de ellos a
+continuación.
+
+#### MRI y YARV
+
+El intérprete de Ruby de Matz (MRI por sus siglas en inglés) fue el estándar de
+facto hasta Ruby 1.8. Cuando Ruby 1.9 fue lanzado otro intérprete ocupó su lugar
+YARV (del Inglés Yet another Ruby VM). YARV ha sido el intérprete por defecto
+desde Ruby 2.0. YARV en cambio sólo provee soporte para «hilos verdes» los
+cuales no son soportados por el systema operativo y no son planificados como
+tareas entre sus núcleos)
+
+La buena noticia, en cambio, es que hay otros intérpretes dispoinbles que pueden
+ayudar a optimizar el hardware para cualquier aplicación hecha a la medida que
+desarrollemos.
+
+#### JRuby
+
+JRuby es una implementación de Ruby que compila el código Ruby y lo convierte en
+el bytecode de Java. Algunos de los beneficios inmediatos son contar con un
+verdadero soporte para multi-hilos, la estabilidad de la plataforma Java, la
+habilidad de llamar, de manera nativa, clases escritas en Java y en algunos
+casos mejor rendimiento. Uno de los principales problemas es que incrementa el
+consumo de memoria; a fin de cuentas es Java.
+
+## Recuros
+
+* [Money][]
+* [Sidekiq][]
+* [Ruby on Rails][]
+* [JRuby][]
+* [Ruby Lang][]
+
+## Recapitulando
+
+Ruby es un lenguaje que fue concebido con la idea de la productividda del
+desarrollador en mente. Ruby on Rails es una plataforma que provee las
+herramientas necesarias para crear aplicaciones de manera muy rápida. Hay una
+gran variedad de gemas (librerías) disponibles para ampliar las características
+de nuestra aplicación.
+
+En el siguiente capítulo discutiremos dos gemas muy populares: Active Record y
+Active Model. Ambas son utilizadas para administrar y hacer persistir datos en
+nustra aplicación.
+
+[Siguiente >>](050-chapter-04.es.md)
+
+ [documentación oficial]: https://rubyonrails.org
+ [Money]: https://rubygems.org/gems/money
+ [Sidekiq]: https://rubygems.org/gems/sidekiq
+ [Ruby on Rails]: https://rubyonrails.org
+ [JRuby]: https://www.jruby.org
+ [Ruby Lang]: https://www.ruby-lang.org
diff --git a/050-chapter-04.es.md b/050-chapter-04.es.md
new file mode 100644
index 0000000..04045ed
--- /dev/null
+++ b/050-chapter-04.es.md
@@ -0,0 +1,93 @@
+### Capítulo 4 - Active Record y Active Model
+
+> Un objeto que encapsula una tabla en una base de datos o en una vista
+encapsula el acceso a dicha base de datos y añade lógica de dominio en esos
+datos - Martin Fowler, Patterns of Enterprise Architecture
+
+## Introducción
+
+El patrón arquitectura de modelo provee la serialización y deserialización de
+datos para nuestra aplicación.
+
+#### Active Model
+
+Active Model es una biblioteca de módulos que preovee varios métodos para Active
+Record. Los módulos de Active Model pueden ser incluídos en nuestras clases
+para proveer la misma funcionalidad. Algunos de los módulos incluídos se
+muestran a continuación.
+
+- AttributeMethods - añade prefijos y sufijos personalizados a los métods de una
+clase.
+- Callbacks - añade los métodos "before", "after" y "around".
+- Conversion - añade los métodos "to_model, "to_key" y "to_param" a un objeto.
+- Dirty - añade métodos para determinar cuando el objeto fue modificado o no.
+- Validations - añade el método "validation" a un objeto.
+- Naming - añade métodos de clase que proveen las versiones plural y singular
+del nombre de la clase.
+- Model - añade métodos de clase para validaciones, traducciones y conversiones,
+así como la habilidad para inicializar un objeto con un hash de atributos.
+- Serialization - añade funcionalidades para facilitar la serialización a y
+deserialización desde un objeto hacia o desde un hash o un objeto JSON.
+- Translation - añade métodos para internationalización usando i18n.
+- Lint Tests - añade funcionalidades para probar cuando nuestro objeto es
+conforma con el API de Active Model.
+- SecurePassword - añade métodos para almacenar passwords o cualquier otro tipo
+de datos de manera segura utilizando la gema "bcrypt".
+
+#### Active Record
+
+El núcleo de una aplicación Rails son sus datos. Las aplicaciones Rails, así
+como muchos otros frameworks similares, son construidos con múltiples capas.
+Rails, en sí, sigue el patrón de arquitectura MVC (modelo-vista-controlador)
+para separar la lógica de programación de la presentación de los datos y del
+procesamiento de los mismos. Active Record es la parte del modelo del patrón
+MVC en Rails. Active Record es donde añadimos funcionamiento y persisten los
+datos requeridos por nuestra aplicación.
+
+Usar Active Record, ya sea en una aplicación Rails u otra independiente,
+nos trae los siguientes beneficios:
+
+* Una forma de representar los modelos y los datos
+* Asociasiones entre modelos
+* Jerarquías a través de modelos relacionados
+* Validación de los datos
+* Acceso a la base de datos utilizando objetos
+
+#### ¿Cuál es la diferencia?
+
+La forma más común de almacenar y obtener datos en una aplicación Rails es
+usando Active Record, el cual encapsula la base de datos y sus relaciones.
+Active Record es, por tanto, una envoltura para recursos tipo REST
+
+Active Model es utilizado por Active Record para la validación de los datos y
+la serialización entre otras funciones.
+
+No todos los modelos necesitan ser modelos de Active Record que mapean alguna
+tabla en la base de datos. Podemos añadir a nuestra aplicación algunos modelos
+cuyos datos persisten en la base de datos, mientras que otros modelos pueden
+mapear puntos de entradas en un API.
+
+Todo lo anterior nos lleva a Active Remote que provee modelos que mapean
+servicios remotos sobre un canal de mensajes. Discutiremos esto en detalles en
+el próximo capítulo.
+
+## Recursos
+
+* [Active Model][]
+* [Active Record][]
+
+## Recapitulando
+
+Hay una variedad de gemas de Ruby disponibles que pueden ayudar a construir
+nuestra capa de modelos de la aplicación. Si seguimos algún tipo de patrón de
+arquitectura orientado al modelo podremos crear modelos respaldados por recursos
+tipo REST, una base de datos o similares. En el próximo capítulo vamos a
+discutir un nuevo tipo de modelo: Active Remote, el cual provee un modelo para
+nuestra aplicación cuyos datos son obtenidos de otro servicio, nos permite
+continuar utlizando el patrón MVC de Rails y nos permite compartir datos entre
+aplicaciones de manera eficiente.
+
+[Next >>](060-chapter-05.es.md)
+
+ [Active Model]: https://guides.rubyonrails.org/active_model_basics.html
+ [Active Record]: https://guides.rubyonrails.org/active_record_basics.html
diff --git a/050-chapter-04.md b/050-chapter-04.md
index 6b727ce..1c62ff6 100644
--- a/050-chapter-04.md
+++ b/050-chapter-04.md
@@ -38,7 +38,7 @@ Active Model is a library of modules that provides various methods to Active Rec
The most common way to persist and retrieve data in a Rails application is with Active Record. Active Record is a wrapper for database tables and their relationships. Active Resource is a wrapper for RESTful resources.
-Active Model is used by Active Record for data validation, serialization and a number of other features.
+Active Model is used by Active Record for data validation, serialization and a number of other features.
Not all models need to be Active Record models that map to a database. We can add models to our app where some models are backed by a database, while another model could map to an API endpoint. This leads us to Active Remote - models that map to a remote service over a message bus. We'll discuss this in detail in the next chapter.
diff --git a/060-chapter-05.es.md b/060-chapter-05.es.md
new file mode 100644
index 0000000..36c1262
--- /dev/null
+++ b/060-chapter-05.es.md
@@ -0,0 +1,89 @@
+### Chapter 5 - Active Remote
+
+> ... La senadora Amidala, antigua reina de Naboo, regresa al Senado Galáctico
+para votar por el tema crítico de crear un Ejercito de la República para asistir
+al Jedi preocupado...
+- La guerra de las Galaxias - Episodio 2 - El ataque de los Clones
+
+## Introducción
+
+Active Remote es una gema ruby que reemplace los modelos de Active Record en
+nuestra aplicación para proveer acceso a otros modelos que existen en otras
+aplicaciones en la red. Con una filosofía similar a Active Resource, provee
+acceso a los datos de recursos remotos.
+
+La diferencia es que mientras Active Resource provee acceso a recursos tipo REST
+Active Remote lo hace utilizando métodos más duraderos y eficientes (Ej
+utilizando un bus de mensajes para durabilidad y Protobuff para una eficiente
+serialización y deserialización de los datos).
+
+## Filosofía
+
+Active Remote intenta proveer una solución para acceder y administrar recursos
+distribuídos brindando un modelo el cual puede ser implementado con una cantidad
+de código mínima. Si el modelo de datos persiste de manera local o en cualquier
+otro lugar no es problema para el resto de la aplicación.
+
+Incluso, ya que Active Remote implementa un sistema de mensajes
+publicador-subscriptor, los clientes no necesitan estar configurados con
+detalles acerca de cuáles servidores poseen o responden a determinados recursos.
+Los clientes sólo necesitan saber cuáles temas publicar hacie el sistema de
+mensaje y que algún otro servidor responderá a sus peticiones.
+
+## Diseño
+
+Durante la inicialización de la aplicación, los modelos de Active Record leen el
+esquema de la base de datos y generan todos los métodos get, set y demás métodos
+los cuáles reducen la cantidad de andamiaje que necesita ser añadido en el
+código de los modelos que heredan de ActiveRecord::Base. Como Active Remote no
+tiene acceso directo a la base de datos, en el lado del cliente tendremos que
+declarar los atributos del model Active Remote usando el método `attribute`. En
+el lado del servidor, donde querramos compartir datos de Active Record,
+tendremos que crear una clase Service para cada modelo que defina puntos de
+acceso que permitan búsqueda, creación, actualización, eliminación, etc.
+
+## Implementación
+
+Active Remote es empacado como una gema ruby que provee un lenguaje específico
+de dominio (DSL por sus siglas en inglés), maneja campos guid de llaves
+primarias y maneja serialización entre otras funcionalidades. La gema Active
+Remote pendende de la gema Protobuf, la cuál será instalada automaticamente
+cuando instalamos o incluímos la gema Active Remote.
+
+Para compartir datos entre servicios, necesitaremos incluir la gema Protobuf
+NATS. Para la aplicación cliente en Rails, Active Remote y Protobuf NATS son las
+gemas que necesitaremos incluir en nuestra aplicación. En la aplicación servidor
+en Rails, incluiremos las gemas Active Remote, Protobuf NATS y Protobuf Active
+Record. Esta última gema se adhiere a las otras 2 proporcionando funcionalidades
+tales como enlazar los mensajes Protobuff con las clases de ACtive REmote.
+
+## Recursos
+
+* https://github.com/abrandoned/protobuf-nats
+* https://github.com/liveh2o/active_remote
+* https://github.com/liveh2o/protobuf-activerecord
+* https://github.com/rails/activeresource
+* https://github.com/ruby-protobuf/protobuf
+
+## Recapitulando
+
+Active Remote nos permite construir una plataforma de comunicación eficiente y
+duradera entre microservicios. También permite seguir una arquitectura bien
+establecida como lo es MVC.
+
+Debido a que Active Remote implementa un bus de mensajes para comunicar entre
+servicios brinda durabilidad a los mismos. Mientras el bus de mensajes
+permanezca en línea la aplicación Rails puede enviar mensajes a cualquier
+servicio y eventualmente recibir respuesta cuando algún servicio caído vuelva a
+estar en línea.
+
+Active Remote también implementa Protobuf como mecanismo eficiente de
+serialización y deserialización. En la medida que la plataforma crece minimizar
+la cantidad de datos de viaja a través del cable paga dividendos mientras
+continuemos escalando la plataforma.
+
+En el próximo capítulo hablaremos de las colas de mensajes, levantaremos un
+servidor NATS para enviar y recibir mensajes simples a través del protocolo
+telnet.
+
+[Siguiente >>](070-chapter-06.es.md)
diff --git a/070-chapter-06.es.md b/070-chapter-06.es.md
new file mode 100644
index 0000000..eaf10ff
--- /dev/null
+++ b/070-chapter-06.es.md
@@ -0,0 +1,167 @@
+### Chapter 6 - Messaging Systems - NATS
+### Capítulo 6 - Sistemas de Mensajes - NATS
+
+> Existen alrededor de 7 octillones de atómos en el cuerpo humano. Eso es una
+cantidad absurda de átomos para desensamblar, retroceder en el espacio-tiempo y
+luego reensamblar en perfecto orden. - Elan Mastai, Todos nuestros presentes
+equivocados
+
+## Introducción
+
+Los sistemas de mensajes son un componente crítico cuando se diseñan sistemas
+distribuidos. Se utilizan para proporcionar a la arquitectura de su plataforma,
+una capa que se utiliza para mezclar mensajes entre servicios. Una capa de
+mensaje proporciona un punto de acceso para que los servicios se comuniquen.
+Cada servicio solo necesita saber cómo comunicarse con la cola de mensajes, a
+qué colas suscribirse y en qué colas escuchar.
+
+NATS es uno de esos sistemas de mensajes que brinda seguridad, flexibilidad,
+es escalable y puede cumplir con los requisitos de rendimiento de la mayoría de
+las plataformas. Al momento de escribir este libro, NATS tiene clientes escritos
+en más de 30 lenguajes de programación.
+
+En este capítulo, pondremos en marcha un servidor NATS. Lo probaremos publicando
+y suscribiéndonos a los mensajes usando un cliente telnet.
+
+## Hagámoslo
+
+Usemos Docker para ejecutar un servidor NATS local y enviarle mensajes.
+Incluiremos una imagen de BusyBox para que podamos ejecutar comandos de telnet
+para probar NATS.
+
+**Ejemplo 6-1** Fichero Docker compose, NATS y BusyBox
+
+```yml
+# ~/projects/nats/docker-compose.yml
+# usage: docker-compose up
+
+version: "3.4"
+
+services:
+ nats:
+ image: nats:latest
+ ports:
+ - 4222:4222
+ - 8222:8222
+ stdin_open: true
+ busybox:
+ image: busybox:latest
+ stdin_open: true
+```
+
+Guarde el archivo con el nombre de archivo `docker-compose.yml`. Ahora cambiemos
+a ese directorio y ejecutemos los contenedores. Las versiones del software y la
+salida pueden diferir de lo que ve en su terminal.
+
+**Ejemplo 6-2** Iniciar NATS y Busybox
+
+```console
+$ cd ~/projects/nats
+$ docker-compose up
+Starting nats_nats_1 ... done
+Starting nats_busybox_1 ... done
+Attaching to nats_busybox_1, nats_nats_1
+nats_1 | [1] 2019/10/07 13:53:36.029873 [INF] Starting nats-server version 2.0.2
+...
+nats_1 | [1] 2019/10/07 13:53:36.032328 [INF] Listening for client connections on 0.0.0.0:4222
+...
+nats_1 | [1] 2019/10/07 13:53:36.033766 [INF] Server is ready
+```
+
+Crear un suscriptor es simple. Abriremos una sesión NATS con telnet. Telnet es
+una aplicación cliente que nos permitirá enviar comandos basados en texto a
+NATS. Proporcionaremos un subject (en el ejemplo 6-3 crearemos un asunto llamado
+'mensajes') y también proporcionaremos un _identificador de suscripción_. El
+identificador de suscripción puede ser un número o una cadena. Usaremos la
+palabra clave `sub` para crear y suscribirnos al subject. Docker Compose
+proporciona un conveniente comando `exec` para conectarse y acceder a un
+contenedor en ejecución. Usaremos el comando `exec` para iniciar sesión en el
+contenedor BusyBox en ejecución y suscribirnos a través de telnet.
+
+**Ejemplo 6-3** Suscribirse a un subject
+
+```console
+$ docker-compose exec busybox sh
+/ # telnet nats 4222 # you'll need to type this line
+...
+sub messages 1 # and this line
++OK # this is the acknowledgement from NATS
+```
+
+Abramos una nueva terminal y creemos un publisher. El cliente del publisher
+deberá proporcionar el nombre del subject en el que desea publicar el mensaje.
+Junto con el subject, el cliente también proporcionará la cantidad de bytes que
+se publicarán. Si falta el número de bytes o es incorrecto, el editor no está
+siguiendo el protocolo NATS y el mensaje será rechazado.
+
+Ejecutemos un comando telnet para publicar mensajes en NATS.
+
+**Ejemplo 6-4** Publicar en un subject
+
+```console
+$ docker-compose exec busybox sh
+/ # telnet nats 4222 # you'll need to type this line
+...
+pub messages 12 # and this line
+Hello WORLD! # and this line
++OK
+```
+
+Deberíamos ver el mensaje `Hello WORLD!` en la ventana de la terminal donde nos
+suscribimos al subject (Ejemplo 6-3). Esto demuestra que tenemos un servidor
+NATS en ejecución, publicamos un mensaje en un subject y nuestro suscriptor
+recibió el mensaje. Puede presionar `Ctrl-C` y luego la letra `e` para salir de
+la sesión de telnet, y luego `Ctrl-D` o escribir `exit` para volver al símbolo
+del sistema de la máquina host.
+
+NATS también proporciona una API de monitoreo que podemos consultar para
+controlar cuántos mensajes se envían a través del servidor, etc. Debido a que
+estamos exponiendo el puerto NATS 8222 fuera del entorno de Docker (consulte el
+archivo `docker-compose.yml` en Ejemplo 6-2), podemos ver el resultado abriendo
+el navegador en nuestra máquina host en la siguiente dirección:
+[http://localhost:8222](http://localhost:8222). Una página debe aparecer en su
+navegador, con un puñado de enlaces. Si tuviéramos que configurar un grupo de
+servidores NATS, aparecerían enlaces adicionales.
+
+En el momento de escribir este libro, hay 5 enlaces en la página. Veamos
+brevemente cada uno de ellos:
+
+* [varz](http://localhost:8222/varz) - Información general acerca del estado
+del servidor y la configuración
+* [connz](http://localhost:8222/connz) - Información más detallada sobre las
+connexiones actuales y las que han sido cerradas.
+* [routez](http://localhost:8222/routez) - Información sobre las rutas activas
+para un clúster.
+* [subsz](http://localhost:8222/subsz) - Información detallada sobre las
+suscripciones actuales y la estructura de datos de enrutamiento.
+* [help](https://docs.nats.io/nats-server/configuration/monitoring) - Un enlace
+a la documentación de NATS.
+
+Algunos de los puntos de acceso anteriores también pueden recibir querystrings
+a la hora de hacer el request, Ej. http://localhost:8222/connz?sort=start,
+lo que ordenará las conexiones por hora de inicio. Consulte la documentación de
+NATS para obtener más información sobre estos puntos de accesoy sus opciones.
+
+## Recursos
+
+* https://docs.docker.com/compose
+* https://hub.docker.com/_/nats
+* https://nats.io
+* https://docs.nats.io/nats-server/configuration/monitoring
+
+## Recapitulando
+
+Los sistemas de mensajes son una capa en una arquitectura de sistema que le
+permite construir una plataforma asincrónica, confiable, desacoplada y
+escalable. NATS es un sistema de mensajes que es simple de configurar y usar.
+
+En este capítulo, levantamos con éxito un servidor NATS local, creamos un
+subject, luego publicamos y nos suscribimos a los mensajes de ese subject.
+También aprendimos sobre la instrumentación que proporciona NATS.
+
+En el próximo capítulo, analizaremos los datos estructurados y qué tipos de
+llaves usar para compartir datos entre sistemas. En el capítulo 9,
+configuraremos un entorno de microservicio que consiste en dos aplicaciones
+Rails que usarán NATS para compartir datos.
+
+[Next >>](080-chapter-07.md)
diff --git a/070-chapter-06.md b/070-chapter-06.md
index 2ad7504..129cbfd 100644
--- a/070-chapter-06.md
+++ b/070-chapter-06.md
@@ -65,7 +65,7 @@ sub messages 1 # and this line
Let's open a new terminal and create a publisher. The publishing client will need to provide the name of the subject it wishes to publish the message on. Along with the subject, the client will also provide the number of bytes that will be published. If the number of bytes is missing or incorrect, the publisher is not following the NATS protocol and the message will be rejected.
-Let's run a telnet command to publish messages to NATS.
+Let's run a telnet command to publish messages to NATS.
**Listing 6-4** Publishing to a Subject
diff --git a/080-chapter-07.es.md b/080-chapter-07.es.md
new file mode 100644
index 0000000..f074dde
--- /dev/null
+++ b/080-chapter-07.es.md
@@ -0,0 +1,135 @@
+### Capítulo 7 - Relaciones entre los datos
+
+## Introducción
+
+Lo más probable es que su aplicación se diseñe con una o más tablas en base de
+datos, ya sea que estén almacenadas en una base de datos relacional o en una
+base de datos NoSQL. A medida que crezca la aplicación, definirá relaciones
+entre entidades que deberán definirse tanto en la aplicación como en la base
+de datos.
+
+## Llaves priamria y foránea
+
+Las llaves primarias son atributos usados para identifiable la unicidad de una
+fila en una relación (también conocida como tabla en la terminología de las
+bases de dato). Las llaves foráneas se utilizan para asociar registros hijos o
+dependientes a otra relación.
+
+## Llaves naturales Vs llaves subrogadas
+
+En la mayoría de las relaciones las filas deben tener un identificador único.
+Un identificador único puede ser un solo atributo, dos o más atributos.
+
+Una llave natural se define como uno o más atributos que identifican de forma
+única una fila. Por ejemplo, en una relación donde almacena la información de
+su empleado, podría usar sus atributos de nombre y apellido como llave natural.
+Pero, ¿qué sucede cuando su empresa contrata a otra persona con el mismo nombre
+y apellido? Cuando llegue ese momento, habrá deseado elegir una mejor llave
+natural, como su Número de Identidad. Podría utilizar el atributo del Número de
+Identidad como llave natural. Los Números de Identidad son únicos y se asignan
+a una persona para toda su vida. Debido a esto, es una excelente llave natural
+porque identifica de manera única a un empleado, pero no cambia como podría
+hacerlo su nombre.
+
+> Nota: No abogamos porque almacene Números de Identidad en su base de datos,
+pero esto sirve como un gran ejemplo de un valor único que proviene de una
+fuente externa. Otra razón para no depender de los Números de Identidad como
+identificadores únicos es que algunos empleados podrían no tener uno asignado
+por la Administración correspondiente.
+
+Su sistema de base de datos genera automáticamente llaves subrogadas. La mayoría
+de las llaves subrogadas son un tipo de número entero que generalmente comienza
+en 1 y aumenta (+1) por cada nueva fila que se inserta.
+
+En teoría se prefieren las llaves naturales a las llaves subrogadas porque están
+compuestas por los datos que se almacenan en la base de datos. Sin embargo, en
+la práctica, las llaves subrogadas suelen ser más fáciles de usar y, por lo
+general, se pueden optimizar para mejorar el rendimiento de la aplicación y la
+base de datos.
+
+## Generadas en Base de datos Vs Generadas por la aplicación
+
+La base de datos o la aplicación pueden generar llaves subrogadas. La base de
+datos suele generar llaves que son de tipo entero, y la concurrencia de la base
+de datos garantiza que cada nuevo registro obtenga un valor único en su columna
+de llave principal, ya sea que tenga una o veinte instancias de la aplicación
+insertando registros en la misma tabla.
+
+### Enteros generados en la base de datos
+
+Las llaves subrogadas más comunes generadas por un sistema de base de datos son
+valores enteros. Al diseñar su base de datos, lo más probable es que necesite
+agregar llaves subrogadas de tipo entero para que Active Record pueda
+administrar los datos. Al diseñar sus tablas, tenga en cuenta que debe construir
+sus estructuras de datos pensando a futuro. De forma predeterminada, el
+scaffolding de Rails genera migraciones que utilizan tipos de datos enteros de
+32 bits. Para la mayoría de las tablas, especialmente las tablas de búsqueda o
+de dominio, este tipo de entero está perfectamente bien. Para algunas de sus
+tablas, después de un tiempo (pueden ser meses o años más tarde), su aplicación
+podría detenerse cuando la llave primaria entera de 32 bits alcance su valor
+máximo.
+
+Cuando esté diseñando su base de datos, si puede identificar qué tablas podrían
+continuar creciendo en tamaño con el tiempo, sería mejor comenzar con un número
+entero de 64 bits: tipos de datos bigint (MySQL) o bigserial (PostgreSQL). Si
+usa un número entero de 64 bits para algunas de sus tablas, recuerde hacer
+coincidir el tipo para las laves foráneas.
+
+### Identificadores únicos
+
+Una forma de generar llaves subrogadas, que a su vez las mantenga únicas, es
+generar identificadores únicos en la aplicación. Estos identificadores únicos
+se pueden compartir con otros servicios. Su diseño podría permitir que la base
+de datos también genere una llave primaria incremental, pero esos valores nunca
+deben compartirse con sistemas fuera de la aplicación.
+
+Los identificadores únicos a veces se denominan identificadores únicos globales
+(GUID, por sus siglas en Inglés) o identificadores únicos universales (UUID por
+sus siglas en Inglés). Los UUID son un subconjunto de GUID. La aplicación puede
+generar GUID antes de que el registro se conserve en la base de datos. A todos
+los efectos prácticos, cada GUID es único. Un GUID de ejemplo es
+`83efd88b-ec78-4e84-b8c7-dad8421d42d4`. Los GUID generalmente se representan
+mediante dígitos hexadecimales separados por guiones. Estos valores se pueden
+compartir como un identificador único para una fila u objeto específico de la
+base de datos entre aplicaciones. También se pueden usar como llaves foráneas
+para mapear objetos primarios y secundarios.
+
+## Cuándo usar cada uno
+
+Si su aplicación es monolítica (una sola aplicación que se ejecuta sobre una
+sola base de datos), por lo general, no hay necesidad de usar incremento
+automático nada más que en la llave primaria. Por defecto, las aplicaciones
+Rails manejan bastante bien la implementación de la llave subrogada de la base
+de datos.
+
+Es en el momento en que decida dividir su base de datos y compartir datos entre
+sistemas que necesitará implementar algún tipo de identificador único que se
+pueda compartir entre sistemas. Una vez que llega a este punto, sus datos ya no
+viven en una sola base de datos o aplicación, sino que se envían y comparten a
+través de la red. Además, si implementa una llave primaria entera generada por
+la base de datos y una llave UUID a medida que crece su plataforma de
+microservicios, el valor entero generado por la base de datos no tiene
+significado fuera de su propia base de datos y aplicación. No es necesario
+compartir el valor entero, solo el UUID.
+
+## Recursos
+
+* https://en.wikipedia.org/wiki/Database_normalization
+* https://en.wikipedia.org/wiki/Universally_unique_identifier
+
+## Recapitulando
+
+La normalización de la base de datos requiere que identifique una llave
+principal. Esta llave podría ser una llave natural (una o más columnas que
+definen los datos) o una llave subrogada generada por la aplicación o la base de
+datos. Rails está diseñado para usar de manera natural llaves primarias
+generadas por bases de datos. A medida que crece su infraestructura y comienza
+a compartir datos con otros sistemas, el uso de un UUID es una opción que hace
+que sus datos sean portátiles e identificables de forma única.
+
+En el próximo capítulo, analizaremos una forma de serializar los datos que se
+compartirán entre nuestros microservicios. Una forma de serializar de manera
+eficiente los datos a través de Protocol Buffers (también conocido como Protobuf
+).
+
+[Siguiente >>](090-chapter-08.es.md)
diff --git a/090-chapter-08.es.md b/090-chapter-08.es.md
new file mode 100644
index 0000000..8ab4948
--- /dev/null
+++ b/090-chapter-08.es.md
@@ -0,0 +1,196 @@
+### Capítulo 8 - Protocol Buffers (Protobuf)
+
+> Los humanos habían desarrollado un modo secuencial de conciencia, mientras que
+los heptápodos habían desarrollado un modo simultáneo de conciencia. Nosotros
+experimentamos los eventos en un orden y percibimos su relación como causa y
+efecto. Ellos experimentaron todos los eventos a la vez y percibieron un
+propósito subyacente a todos. Un propósito minimizador y maximizador.
+- Ted Chiang, La historia de tu vida
+
+## Introducción
+
+¿Por qué ha creado su aplicación? Lo más probable es que la razón sea que
+necesitaba rastrear algunos datos. Es posible que usted o alguien de su empresa
+haya comenzado con una hoja de cálculo, pero con el tiempo se dio cuenta de que
+el seguimiento de los datos en una hoja de cálculo se volvió engorroso y ya no
+satisfacía sus necesidades. Entonces, nació una aplicación. Su nueva aplicación
+lista para usar luego comenzó a crecer, con relaciones entre entidades de datos.
+A medida que aumentaba la cantidad de datos, la cantidad de relaciones y los
+requisitos de procesamiento, decidió que necesitaba dividir su aplicación en
+servicios separados. Cuando es necesario compartir un dato entre aplicaciones
+debemos tomar un par de decisiones. ¿Qué atributos se compartirán? ¿Cómo
+accederán los clientes a los datos? ¿Qué aplicación poseerá y conservará los
+datos? ¿Cómo podemos facilitar la ampliación o la adición de nuevos atributos a
+nuestras entidades y que siga siendo compatibles con las versiones anteriores?
+
+Cuando crea una plataforma de microservicios, debe tomar varias decisiones, una
+de ellas es cómo compartimos datos entre servicios. Como se discutió en el
+capítulo 2, Protocol buffers (también conocidos como protobuf) es una de esas
+opciones.
+
+Desarrollado por Google, protobuf es un método de serialización de datos en un
+formato binario que permite el rendimiento sobre la flexibilidad. Protobuf tiene
+una estructura estándar para definir mensajes. Los compiladores están
+disponibles para convertir las definiciones en clases o estructuras que son
+específicas del lenguaje. Por ejemplo, el mismo archivo de definición se puede
+usar para generar clases o estructuras tanto para Java como para Ruby, de modo
+que las aplicaciones escritas en ambos lenguajes puedan compartir el mismo
+mensaje.
+
+## Filosofía
+
+Protobuf serializa los datos en un formato binario que no se describe a sí mismo
+. Por el contrario, un objeto JSON o XML suele ser legible y editable por
+humanos, cada objeto se puede inspeccionar y el desarrollador puede ver los
+nombres de los campos y sus valores. Protobuf es un formato binario. Implementa
+un formato de campo ordenado y ambos servicios que comparten el mensaje
+necesitan conocer la estructura del mensaje.
+
+Hay muchas formas de codificar y compartir datos. XML, JSON y otros formatos
+están bien definidos y son fáciles de generar y consumir. Pero, ¿qué sucede si
+desea un mejor rendimiento de codificación y decodificación? ¿Qué sucede si
+desea reducir la carga de la red de mensajes que se transmiten entre sistemas?
+¿Qué pasa si su plataforma, la cantidad de desarrolladores y la cantidad de
+mensajes que pasan entre sistemas crecen de la noche a la mañana?
+
+Protobuf intenta resolver estos y otros problemas codificando datos en un
+formato binario (que, por supuesto, es mucho más pequeño que un objeto
+codificado XML o JSON). Las definiciones de Protobuf constan de uno o más
+campos numerados de forma única. A cada campo codificado se le asigna un
+número de campo y un valor. Este número de campo es lo que diferencia a
+Protobuf de otros objetos. El número de campo se usa para codificar y
+decodificar los atributos del mensaje, reduce la cantidad de datos que deben
+codificarse al omitir el nombre del atributo y permite la capacidad de
+extensión para que los desarrolladores puedan agregar campos a una definición
+sin tener que actualizar todas las aplicaciones que consumen ese mensaje al
+mismo tiempo. Esto es posible porque las aplicaciones existentes ignorarán los
+nuevos campos en cualquier mensaje que reciban y decodifiquen.
+
+## Implementación
+
+A continuación se muestra un ejemplo de definición de Protobuf. Los archivos
+Protobuf tienen la extensión de archivo `.proto`.
+
+**Ejemplo 8-1** Mensaje protobuf Employee
+
+```proto
+// file employee.proto
+1 syntax = "proto3";
+2
+3 message Employee {
+4 string guid = 1;
+5 string first_name = 2;
+6 string last_name = 3;
+7 }
+```
+
+Inspeccionemos cada línea.
+
+La línea 1 define la versión de la sintaxis de Protobuf que nos gustaría usar.
+
+La línea 3 es el comienzo de nuestra declaración de mensaje.
+
+Las líneas 4-6 son las definiciones de campo. Cada línea tiene un tipo (el campo
+GUID es un tipo de cadena). La línea 4 tiene un nombre de atributo de `guid` y
+el número de campo de 1.
+
+Esta definición de Protobuf no se utiliza así en su aplicación. Lo que haremos a
+continuación es compilar este archivo `employee.proto` en una clase o archivo de
+estructura similar al lenguaje en el que está escrita su aplicación, ya sea Java
+, C#, Go o Ruby. Si admite una plataforma heterogénea con varios idiomas, es
+posible que desee crear secuencias de comandos que compilarán automáticamente
+sus archivos `.proto` en los lenguajes requeridos cada vez que agregue un nuevo
+archivo `.proto` o agregue un nuevo campo a uno de sus definiciones existentes.
+
+Cubrimos brevemente la [implementación de Ruby en el capítulo 2][], pero
+repasemos y sigamos en más detalle aquí.
+
+El siguiente ejemplo es el resultado de la implementación de Ruby (se pueden
+encontrar configuraciones y detalles adicionales en
+[chapter 9][]. Después de definir el archivo de definición `.proto` y ejecutar
+el comando `rake protobuf:compile`, ahora tendremos archivos similares a los
+siguientes:
+
+**Ejemplo 8-2** Clase Employee protobuf en Ruby
+
+```ruby
+# file employee.pb.rb
+class Employee < ::Protobuf::Message
+ optional :string, :guid, 1
+ optional :string, :first_name, 2
+ optional :string, :last_name, 3
+end
+```
+
+**Datos serializados**
+
+Note que los datos siguientes corresponden con la representación en cadena de
+caracteres de la codificación binaria.
+
+**Ejemplo 8-3** Codificación de Employee protobuf
+
+```console
+# Employee
+\n$d4b3c75c-2b0c-4f74-87d7-651c5ac284aa\x12\x06George\x1A\bCostanza
+```
+
+Hay un par de cosas a tener en cuenta en esta cadena. La primera es que no se
+desperdicia espacio al definir los nombres de los campos. Los números al final
+de la línea en los archivos `.proto` y `.rb` indican el índice del campo.
+Cuando se serializan los datos en el mensaje protobuf, los datos se empaquetan
+en un orden secuencial sin los nombres del campo. Se utiliza un delimitador
+para separar los campos, que siempre estarán en el mismo orden. Ocasionalmente,
+es posible que necesitemos deprecar o eliminar un campo. Debido a que los campos
+están indexados, el índice del campo que debe eliminarse siempre ocupará ese
+espacio y nunca debemos reutilizar ese número de índice. Si tuviéramos que
+reutilizar el número de índice, los servicios que todavía usan la definición
+anterior malinterpretarían los datos en esa posición y las cosas pueden salir
+mal, especialmente cuando se modifica el tipo de datos pero se reutiliza el
+índice del campo (por ejemplo, si el tipo de datos cambia de un int32 a un bool)
+.
+
+Depende del receptor conocer los índices y sus nombres de campo relacionados al
+deserializar el mensaje. Esto tiene la ventaja de requerir menos ancho de banda
+de red para entregar el mensaje en comparación con otras estructuras de mensajes
+como JSON o XML. Otra ventaja es que cuando se deserializa el mensaje protobuf,
+se ignoran los campos adicionales que no están definidos en la clase protobuf.
+Esto hace que la plataforma sea mantenible, porque los remitentes y los
+receptores se pueden actualizar e implementar de forma independiente.
+
+Por ejemplo, un remitente puede tener una nueva clase `::Protobuf::Message` que
+agrega nuevos campos a un mensaje protobuf. Cuando este mensaje es recibido por
+otro servicio, los nuevos campos serán ignorados por cualquier receptor que esté
+usando una versión anterior de la clase `::Protobuf::Message`. Un receptor
+también se puede modificar de forma independiente para esperar un nuevo campo,
+pero si no está definido en el protomensaje del remitente, el campo se marca
+como `nil` (o el valor equivalente cero o `nil` del lenguaje). En estos
+ejemplos, existe la posibilidad de que se pierdan los datos de los nuevos campos
+, por lo que es posible que desee actualizar los receptores antes de actualizar
+los remitentes. Este diseño le permite actualizar los servicios de forma
+independiente sin el riesgo de romper las aplicaciones receptoras porque no
+están listas para recibir los campos recién definidos.
+
+## Recursos
+
+* https://developers.google.com/protocol-buffers
+* https://github.com/ruby-protobuf/protobuf/wiki/Compiling-Definitions
+
+## Recapitulando
+
+Protobuf es una forma eficiente de empaquetar y compartir datos entre servicios
+. Es agnóstico del lenguaje y extensible. Debido a que son independientes del
+lenguaje, no está limitado a crear servicios en un solo lenguaje de programación
+en su plataforma. Por ejemplo, puede escribir sus aplicaciones de línea de
+negocio internas en Rails mientras escribe sus algoritmos de procesamiento de
+datos en R.
+
+En el próximo capítulo, pondremos en marcha un entorno limitado de desarrollo
+con NATS y Rails. Crearemos dos aplicaciones Rails, una que posee una base de
+datos y comparte los datos a través de Protobuf y Active Remote y otra que
+actúa como un cliente que puede recuperar y modificar los datos en la primera
+aplicación.
+
+[Siguiente >>](100-chapter-09.es.md)
+
+ [implementación de Ruby en el capítulo 2]: https://github.com/kevinwatson/rails-microservices-book/blob/master/030-chapter-02.es.md#protocol-buffers
+ [chapter 9]: https://github.com/kevinwatson/rails-microservices-book/blob/master/100-chapter-09.es
diff --git a/100-chapter-09.es.md b/100-chapter-09.es.md
new file mode 100644
index 0000000..2e063e4
--- /dev/null
+++ b/100-chapter-09.es.md
@@ -0,0 +1,677 @@
+### Capítulo 9 - El sandbox del microservicio Active Remote
+
+> Intento formular un plan pero mis pensamientos son una mezcla tóxica de
+arrepentimiento, pánico y autodesprecio; como si alguien agitara una botella
+de refresco carbonatado y la destapara dentro de mi cerebro.
+- Elan Mastai, Todos nuestros presentes equivocados
+
+## Introducción
+
+Antes de que podamos sumergirnos en la construcción de nuestro entorno
+distribuido, primero necesitaremos configurar nuestro entorno de desarrollo.
+Usaremos Docker para comenzar a funcionar rápidamente. Docker también
+proporciona su propia red aislada de manera que no interferirá con los procesos
+que ya se ejecutan en su computadora de desarrollo.
+
+Usaremos los términos imágenes y contenedores. Si bien estos términos a veces se
+usan indistintamente, existen claras diferencias. Las imágenes de Docker son la
+estructura de archivos de una aplicación en su disco duro (piense en una imagen
+como los archivos en la carpeta de su proyecto). Un contenedor Docker es una
+instancia en ejecución de la imagen (piense en un contenedor como la instancia
+de su aplicación). Se pueden crear uno o más contenedores a partir de una sola
+imagen, siempre que se hayan proporcionado nombres de servicios separados. Otro
+ejemplo es que puede ejecutar varias instancias de una aplicación Rails desde
+el mismo directorio especificando un número de puerto diferente (por ejemplo,
+`rails server -p 3000` y `rails server -p 3001`).
+
+El entorno sandbox que crearemos en este capítulo utilizará la gema Active
+Remote para levantar un cliente (llamaremos a esta aplicación `active-remote`)
+que puede acceder a datos en el servicio que los posee (llamaremos esta
+aplicación `active-record` porque usará Active Record para conservar los datos
+en una base de datos a la que solo él tiene acceso).
+
+En este entorno, usaremos NATS para pasar mensajes entre nuestros microservicios
+. Se enviará una solicitud para crear un nuevo empleado desde la aplicación
+Active Remote a NATS, y NATS reenviará la solicitud a cualquier servicio que se
+haya suscrito a esa ruta. La aplicación Active Record que creamos se suscribirá
+y responderá con la instancia de empleado recién creada envuelta en un mensaje
+de Protobuf.
+
+_**Imagen 9-1**_ Pasando mensajes de Active Remote
+
+
+
+## Instalando Docker
+
+Si ya tienes Docker y Docker Compose instalados, ¡genial! De lo contrario,
+deberá seguir los pasos a continuación.
+
+Si está utilizando Windows o macOS, descargue e instale Docker Desktop. Los
+enlaces de descarga y las instrucciones se pueden encontrar aquí:
+https://www.docker.com/products/docker-desktop.
+
+También usaremos Docker Compose para configurar y ejecutar varias aplicaciones
+desde un único archivo de configuración. Docker Compose está incluido en Docker
+Desktop para macOS y Windows. Si está ejecutando Linux, deberá instalar Docker
+por separado y luego seguir las instrucciones de instalación de Docker Compose
+que se encuentran aquí: https://docs.docker.com/compose/install.
+
+## Implementación
+
+### Requisitos
+
+* NATS
+* Ruby
+* Ruby gems
+ * Active Remote
+ * Protobuf
+ * Rails
+* SQLite
+
+Debido a que instalamos Docker Desktop, no es necesario instalar Ruby, Ruby on
+Rails, NATS o SQLite en su computadora. Se instalarán dentro de las imágenes y
+contenedores de Docker que desarrollaremos a continuación.
+
+#### Probando nuestra instalación de Docker y Docker Compose
+
+Podemos probar nuestra instalación ejecutando los comandos `docker version` y
+`docker-compose --version`. Las versiones que ve en su resultado pueden diferir
+de las versiones que ve a continuación.
+
+**Ejemplo 9-2** Verificando el entorno de Docker
+
+```console
+$ docker version
+Client: Docker Engine - Community
+ Version: 19.03.5
+...
+Server: Docker Engine - Community
+ Engine:
+ Version: 19.03.5
+
+$ docker-compose --version
+docker-compose version 1.24.1
+```
+
+Si ve algún error, verifique la instalación de Docker Desktop.
+
+### Estructurfe de directory del projecto
+
+Ahora necesitaremos crear un directorio para nuestro proyecto. A medida que
+avance, creará tres subdirectorios, uno para nuestros mensajes Protobuf
+compartidos, uno para nuestra aplicación de servidor ActiveRecord Ruby on Rails
+que almacena los datos en una base de datos SQLite y otro para nuestra
+aplicación cliente ActiveRemote que proporcionará una front-end para nuestro
+servicio ActiveRecord.
+
+Siguiendo este tutorial, debería terminar con los siguientes directorios (y
+muchos archivos y directorios en cada directorio). El directorio
+`rails-microservices-sample-code` lleva el nombre del repositorio de GitHub
+que se encuentra en
+https://github.com/kevinwatson/rails-microservices-sample-code.
+
+* rails-microservices-sample-code
+ * chapter-09
+ * active-record
+ * active-remote
+ * protobuf
+
+### Configurando un entorno de desarrollo
+
+Comencemos creando un archivo Dockerfile y un archivo Docker Compose. Usaremos
+el archivo Dockerfile para crear una imagen con las aplicaciones de línea de
+comandos que necesitamos y usaremos un archivo de configuración de Docker
+Compose para reducir la cantidad de parámetros que necesitaremos usar para
+ejecutar cada comando. La alternativa es simplemente usar un Dockerfile y
+comandos `docker` relacionados.
+
+Cree el siguiente archivo Dockerfile en el directorio
+`rails-microservices-sample-code`. Usaremos el nombre `Dockerfile.builder` para
+diferenciar el Dockerfile que usaremos para generar nuevos servicios Rails del
+Dockerfile que usaremos para construir y ejecutar nuestras aplicaciones Rails.
+
+Nota: La primera línea de estos archivos es un comentario y se utiliza para
+indicar la ruta y el nombre del archivo. Esta línea se puede omitir del archivo.
+
+_**Ejemplo 9-3**_ Fichero Dockerfile utilizado para crear una imagen que será
+utiliazada para generar nuestra aplicación Rails.
+
+```dockerfile
+# rails-microservices-sample-code/Dockerfile.builder
+
+FROM ruby:2.6.5
+
+RUN apt-get update && apt-get install -qq -y --no-install-recommends \
+ build-essential \
+ protobuf-compiler \
+ nodejs \
+ vim
+
+WORKDIR /home/root
+
+RUN gem install rails -v 5.2.4
+RUN gem install protobuf
+```
+
+Cree el siguiente archivo `docker-compose.builder.yml` en el directorio
+`rails-microservices-sample-code`. Usaremos este archivo de configuración para
+inicilizar nuestro entorno de desarrollo con todas las herramientas de línea de
+comandos que necesitaremos.
+
+_**Ejemplo 9-4**_ Fichero Docker Compose para iniciar el contenedor que vamos
+a utilizar para generar nuestra aplicación Rails.
+
+```yaml
+# rails-microservices-sample-code/docker-compose.builder.yml
+
+version: "3.4"
+
+services:
+ builder:
+ build:
+ context: .
+ dockerfile: Dockerfile.builder
+ volumes:
+ - .:/home/root
+ stdin_open: true
+ tty: true
+```
+
+Comencemos e iniciemos sesión en el contenedor builder. Luego ejecutaremos los
+comandos de generación de Rails desde el contenedor, lo que creará dos
+aplicaciones Rails. Debido a que hemos asignado un volumen en el archivo `.yml`
+anterior, los archivos que se generan se guardarán en el directorio
+`rails-microservices-sample-code`. Si no asignamos un volumen, los archivos que
+generamos solo existirían dentro del contenedor, y cada vez que detengamos y
+reiniciemos el contenedor, será necesario regenerarlos. Mapear un volumen a un
+directorio en la computadora host servirá archivos a través del entorno del
+contenedor, que incluye una versión específica de Ruby, Rails y las gemas que
+necesitaremos para ejecutar nuestras aplicaciones.
+
+```console
+$ docker-compose -f docker-compose.builder.yml run builder bash
+```
+
+El comando `run` de Docker Compose construirá la imagen (si aún no se creó),
+iniciará el contenedor, ingresará al contenedor en ejecución y nos brindará un
+símbolo de sistema usando el shell `bash`.
+
+Ahora debería ver que ha iniciado sesión como usuario root en el contenedor
+(verá un mensaje que comienza con un hash `#`). Iniciar sesión como usuario root
+suele estar bien dentro de un contenedor, porque el aislamiento del entorno del
+contenedor limita lo que el usuario root puede hacer.
+
+### Protobuf
+
+Ahora creemos un mensaje Protobuf y compilaremos el archivo `.proto` para
+generar el archivo Ruby relacionado, que contiene las clases que se copiarán en
+cada una de nuestras aplicaciones Ruby on Rails. Este archivo definirá el
+mensaje de Protobuf, las solicitudes y las definiciones de llamadas a
+procedimientos remotos.
+
+Cree un par de directorios para nuestros archivos de entrada y salida. El
+siguiente comando `mkdir -p` creará directorios con la siguiente estructura:
+
+* protobuf
+ * definitions
+ * lib
+
+```console
+$ mkdir -p protobuf/{definitions,lib}
+```
+
+Nuestro fichero de definición Protobuf:
+
+_**Ejemplo 9-5**_ Fichero protobuf del mensaje Employee
+
+```protobuf
+# rails-microservices-sample-code/protobuf/definitions/employee_message.proto
+
+syntax = "proto3";
+
+message EmployeeMessage {
+ string guid = 1;
+ string first_name = 2;
+ string last_name = 3;
+}
+
+message EmployeeMessageRequest {
+ string guid = 1;
+ string first_name = 2;
+ string last_name = 3;
+}
+
+message EmployeeMessageList {
+ repeated EmployeeMessage records = 1;
+}
+
+service EmployeeMessageService {
+ rpc Search (EmployeeMessageRequest) returns (EmployeeMessageList);
+ rpc Create (EmployeeMessage) returns (EmployeeMessage);
+ rpc Update (EmployeeMessage) returns (EmployeeMessage);
+ rpc Destroy (EmployeeMessage) returns (EmployeeMessage);
+}
+```
+
+Para compilar los archivos `.proto`, usaremos una tarea Rake proporcionada por
+la gema `protobuf`. Para acceder a las tareas Rake de la gema `protobuf`,
+necesitaremos crear un `Rakefile`. ¡Hagámoslo!
+
+_**Ejemplo 9-6**_ Rakefile
+
+```ruby
+# rails-microservices-sample-code/protobuf/Rakefile
+
+require 'protobuf/tasks'
+```
+
+Ahora podemos ejecutar la tarea Rake `compile` para generar el fichero.
+
+```console
+$ mkdir chapter-09 # create a directory for this chapter
+$ docker-compose -f docker-compose.builder.yml run builder bash
+# cd protobuf
+# rake protobuf:compile
+```
+
+Esto generará un archivo llamado `employee_message.pb.rb` en el directorio
+`protobuf/lib`. Copiaremos este archivo en el directorio `app/lib` en las
+aplicaciones Rails que crearemos a continuación.
+
+### Crear una aplicación Rails con una base de datos
+
+La primera aplicación Rails que generaremos tendrá un modelo de Active Record y
+podrá conservar los registros en una base de datos SQLite. Agregaremos las gemas
+`active_remote`, `protobuf-nats` y `protobuf-activerecord` al archivo `Gemfile`.
+Luego ejecutaremos el comando `bundle` para obtener las gemas del repositorio
+[Rubygems][]. Después de obtener las gemas, crearemos un andamiaje para una
+entidad Employee y generaremos una tabla "employees" en la base de datos SQLite.
+Podríamos conectar nuestra aplicación a una base de datos PostgreSQL o MySQL,
+pero para los propósitos de esta aplicación de demostración, la base de datos
+SQLite basada en archivos es suficiente. Por supuesto, la aplicación Active
+Remote que generamos no sabrá ni le importará cómo se conservan los datos (o si
+los datos persisten).
+
+Generemos la aplicación Rails que actuará como servidor y poseedor de los datos.
+Como poseedor de los datos, puede conservarlos en una base de datos. Llamaremos
+a esta aplicación "active-record".
+
+```console
+# cd chapter-09
+# rails new active-record
+# cd active-record
+# echo "gem 'active_remote'" >> Gemfile
+# echo "gem 'protobuf-nats'" >> Gemfile
+# echo "gem 'protobuf-activerecord'" >> Gemfile
+# bundle
+# rails generate scaffold Employee guid:string first_name:string last_name:string
+# rails db:migrate
+# exit
+```
+
+Asegúrese de inspeccionar el resultado de cada uno de los comandos anteriores en
+busca de errores. Si se encuentran errores, vuelva a verificar cada comando en
+busca de errores tipográficos o caracteres adicionales.
+
+Personalicemos la aplicación para servir a nuestra entidad Employee a través de
+Protobuf. Necesitaremos un directorio `app/lib` y luego copiaremos el archivo
+`employee_message.pb.rb` generado a este directorio.
+
+```console
+$ mkdir chapter-09/active-record/app/lib
+$ cp protobuf/lib/employee_message.pb.rb chapter-09/active-record/app/lib/
+```
+
+A continuación, necesitaremos crear una clase de servicio para definir cómo
+manejar los puntos de acceso del servicio de llamada a procedimiento remoto que
+definimos en el archivo `.proto`. Necesitaremos crear un directorio
+`app/services`. Luego agregaremos un archivo
+`app/services/employee_message_service.rb` para volver a abrir la clase
+`EmployeeMessageService` definida en nuestro archivo
+`app/lib/employee_message.pb.rb` para proporcionar detalles de implementación.
+Por último, definiremos algunos scpes y field_scopes en nuestro
+`app/models/employee.rb` para conectar los atributos del modelo existente con
+los atributos de protobuf.
+
+_**Ejemplo 9-6**_ Employee, un modelo de Active Record.
+
+```ruby
+# rails-microservices-sample-code/chapter-09/active-record/app/models/employee.rb
+
+require 'protobuf'
+
+class Employee < ApplicationRecord
+ protobuf_message :employee_message
+
+ scope :by_guid, ->(*values) { where(guid: values) }
+ scope :by_first_name, ->(*values) { where(first_name: values) }
+ scope :by_last_name, ->(*values) { where(last_name: values) }
+
+ field_scope :guid
+ field_scope :first_name
+ field_scope :last_name
+end
+```
+
+```console
+$ mkdir chapter-09/active-record/app/services
+```
+
+_**Ejemplo 9-7**_ La clase EmployeeMessageService.
+
+```ruby
+# rails-microservices-sample-code/chapter-09/active-record/app/services/employee_message_service.rb
+
+class EmployeeMessageService
+ def search
+ records = ::Employee.search_scope(request).map(&:to_proto)
+
+ respond_with records:
+ end
+
+ def create
+ record = ::Employee.create(request)
+
+ respond_with record
+ end
+
+ def update
+ record = ::Employee.where(guid: request.guid).first
+ record.assign_attributes(request)
+ record.save!
+
+ respond_with record
+ end
+
+ def destroy
+ record = ::Employee.where(guid: request.guid).first
+
+ record.delete
+ respond_with record.to_proto
+ end
+end
+```
+
+También necesitaremos agregar algunos detalles más. Debido a que el archivo
+`app/lib/employee_message.pb.rb` contiene varias clases, solo se carga la clase
+que coincide con el nombre del archivo. En el modo de desarrollo, Rails puede
+cargar archivos de forma diferida (lazy loading) siempre que el nombre del
+archivo pueda inferirse del nombre de la clase, Ej: El código que requiere la
+clase `EmployeeMessageService` intentará cargar de forma diferida un archivo
+llamado `employee_message_service.rb` y lanzará un error si no se encuentra
+el archivo. Podemos separar las clases en el archivo
+`app/lib/employee_message.pb.rb` en archivos separados o habilitar la carga
+inmediata (eager load) en la configuración. Para los propósitos de esta
+demostración, habilitemos la carga inmediata.
+
+_**Ejemplo 9-8**_ Fichero de configurarión de desarrollo
+
+```ruby
+# rails-microservices-sample-code/chapter-09/active-record/config/environments/development.rb
+
+...
+config.eager_load = true
+...
+```
+
+El último cambio que debemos hacer en la aplicación `active-record` es agregar
+un archivo de configuración `protobuf_nats.yml` para configurar el código
+proporcionado por la gema `protobuf-nats`.
+
+_**Ejemplo 9-9**_ Fichero de configuración Protobuf Nats
+
+```yml
+# rails-microservices-sample-code/chapter-09/active-record/config/protobuf_nats.yml
+
+default: &default
+ servers:
+ - "nats://nats:4222"
+
+development:
+ <<: *default
+```
+
+### Crear una aplicación Rails sin base de datos
+
+Ahora es momento de crear nuestra segunda aplicación Rails. A esta la llamaremos
+"active-remote". Tendrá un modelo, pero las clases del modelo heredarán de
+`ActiveRemote::Base` en lugar del `ApplicationRecord` predeterminado (que hereda
+de `ActiveRecord::Base`). En otras palabras, estos modelos interactuarán con los
+modelos "active-remote" enviando mensajes a través del servidor NATS.
+
+Generemos la aplicación "active-remote". No necesitaremos la capa de
+persistencia de Active Record, por lo que usaremos el indicador
+`--skip-active-record`. Necesitaremos las gemas `active_remote` y
+`protobuf-nats`, pero no la gema `protobuf-activerecord` que incluimos en la
+aplicación `active-record`. Usaremos el andamiaje Rails para generar un modelo,
+un controlador y vistas para ver y administrar nuestra entidad Empleado que se
+compartirá entre las dos aplicaciones.
+
+```console
+$ docker-compose -f docker-compose.builder.yml run builder bash
+# cd chapter-09
+# rails new active-remote --skip-active-record
+# cd active-remote
+# echo "gem 'active_remote'" >> Gemfile
+# echo "gem 'protobuf-nats'" >> Gemfile
+# bundle
+# rails generate scaffold Employee guid:string first_name:string last_name:string
+```
+
+Necesitaremos realizar un par de cambios en la aplicación "active-remote".
+Primero, copiemos el archivo Protobuf.
+
+```console
+$ mkdir chapter-09/active-remote/app/lib
+$ cp protobuf/lib/employee_message.pb.rb chapter-09/active-remote/app/lib/
+```
+
+Ahora agreguemos un modelo que hereda de Active Remote.
+
+_**Ejemplo 9-10**_ Clase Employee Active Remote
+
+```ruby
+# rails-microservices-sample-code/chapter-09/active-remote/app/models/employee.rb
+
+class Employee < ActiveRemote::Base
+ service_class ::EmployeeMessageService
+
+ attribute :guid
+ attribute :first_name
+ attribute :last_name
+end
+```
+
+Ahora editemos el fichero `config/environments/development.rb` para habilitar la
+carga inmediata por las mismas razones antes mencionadas.
+
+_**Ejemplo 9-11**_ Fichero de configuración de desarrollo
+
+```ruby
+# rails-microservices-sample-code/chapter-09/active-remote/config/environments/development.rb
+
+...
+config.eager_load = true
+...
+```
+
+Añadamos el fichero `protobuf_nats.yml`.
+
+_**Ejemplo 9-12**_ Fichero de configuración Protobuf Nats
+
+```yml
+# rails-microservices-sample-code/chapter-09/active-record/config/protobuf_nats.yml
+
+default: &default
+ servers:
+ - "nats://nats:4222"
+
+development:
+ <<: *default
+```
+
+Lo último que debemos hacer es cambiar un par de llamadas a métodos en el
+archivo `employees_controller.rb` para cambiar la forma en que se obtienen y
+crean instancias de nuestros mensajes Protobuf. Necesitamos usar el método
+`search` en lugar de los métodos predeterminados `all` y `find` de Active Record
+. Además, debido a que utilizamos uuids (guids) como clave única entre servicios
+, generaremos un nuevo uuid cada vez que se llame a la acción "new".
+
+_**Ejemplo 9-13**_ Clase Employee controller
+
+```ruby
+# rails-microservices-sample-code/chapter-09/active-remote/controllers/employees_controller.rb
+
+ def index
+ @employees = Employee.search({})
+ end
+
+...
+
+def new
+ @employee = Employee.new(guid: SecureRandom.uuid)
+end
+
+...
+
+def set_employee
+ @employee = Employee.search(guid: params[:id]).first
+end
+```
+
+### Crear y configurar nuestro entorno
+
+Por último, pero no menos importante, agreguemos un archivo `Dockerfile` y
+`docker-compose.yml` para crear una imagen, activar contenedores y vincular
+nuestros servicios.
+
+_**Ejemplo 9-14**_ Fichero Dockerfile del sandbox
+
+```dockerfile
+# rails-microservices-sample-code/Dockerfile
+
+FROM ruby:2.6.5
+
+RUN apt-get update && apt-get install -qq -y --no-install-recommends build-essential nodejs
+
+ENV INSTALL_PATH /usr/src/service
+ENV HOME=$INSTALL_PATH PATH=$INSTALL_PATH/bin:$PATH
+RUN mkdir -p $INSTALL_PATH
+WORKDIR $INSTALL_PATH
+
+RUN gem install rails -v 5.2.4
+
+ADD Gemfile* ./
+RUN set -ex && bundle install --no-deployment
+```
+
+_**Ejemplo 9-15**_ Fichero Docker Compose del sandbox
+
+```yml
+# rails-microservices-sample-code/chapter-09/docker-compose.yml
+
+version: "3.4"
+
+services:
+ active-record:
+ environment:
+ - PB_SERVER_TYPE=protobuf/nats/runner
+ build:
+ context: ./active-record
+ dockerfile: ../../Dockerfile
+ command: bundle exec rpc_server start -p 9399 -o active-record ./config/environment.rb
+ volumes:
+ - ./active-record:/usr/src/service
+ depends_on:
+ - nats
+ active-remote:
+ environment:
+ - PB_CLIENT_TYPE=protobuf/nats/client
+ build:
+ context: ./active-remote
+ dockerfile: ../../Dockerfile
+ command: bundle exec puma -C config/puma.rb
+ ports:
+ - 3000:3000
+ volumes:
+ - ./active-remote:/usr/src/service
+ depends_on:
+ - nats
+ nats:
+ image: nats:latest
+ ports:
+ - 8222:8222
+```
+
+### Ejecutando las aplicaciones
+
+¡Felicidades! Sus aplicaciones ahora están configuradas y listas para ejecutarse
+en un contenedor de Docker. Ejecute el siguiente comando para descargar las
+imágenes requeridas, cree una nueva imagen que será utilizada por ambos
+contenedores de Rails e inicie tres servicios: `active-record`, `active-remote`
+y `nats`.
+
+```console
+$ cd chapter-09
+$ docker-compose up
+```
+
+Puede que tarde unos minutos, pero una vez que todos los contenedores estén en
+funcionamiento, podemos navegar hasta http://localhost:3000/employees. La
+aplicación Rails que se ejecuta en el puerto 3000 es la aplicación Active Remote
+.
+
+### Monitorizando
+
+Si revisamos el log en la consola donde ejecutó el comando `docker-compose up`,
+deberíamos ver una salida como la siguiente:
+
+```console
+active-remote_1 | I, [2019-12-28T00:35:06.460838 #1] INFO -- : [CLT] - 6635f4080982 - 2aca3d71d6d0 - EmployeeMessageService#search - 48B/75B - 0.0647s - OK - 2019-12-28T00:35:06+00:00
+```
+
+Esto indica que se llamó al método `EmployeeMessageService#search`. No todos los
+resultados de los servicios se muestran en la salida por consola.
+
+Continúe y haga clic en el enlace "New Employee". Complete los campos Nombre y
+Apellido y haga clic en el botón "Create Employee" para crear un nuevo registro
+de empleado. Revise los registros nuevamente. Debería ver un mensaje como el
+siguiente.
+
+```console
+active-remote_1 | I, [2019-12-28T00:40:43.597089 #1] INFO -- : [CLT] - 0d6886451aa0 - 3f910c005424 - EmployeeMessageService#create
+```
+
+También podemos verificar la información de conexión NATS para verificar que los
+datos se pasan a través del servidor NATS. Vaya a http://localhost:8222 y haga
+clic en el enlace 'connz'. Al hacer clic en los enlaces para extraer datos en la
+página http://localhost:3000/employees se pasarán mensajes adicionales a la
+aplicación `active-record` a través del servidor NATS. Al actualizar la página
+http://localhost:8222/connz se mostrarán contadores incrementales en los campos
+`num_connections`, `num_connections/in_msgs` y `num_connections/out_msgs`.
+
+## Recursos
+
+* https://docs.docker.com/compose
+* https://nats.io
+* https://www.sqlite.org
+
+## Recapitulando
+
+Ahora que ha configurado y puesto en marcha dos nuevos servicios que pueden
+comunicarse y compartir datos a través de Protobuf, siéntase libre de
+experimentar agregando nuevos mensajes de Protobuf, llamadas a procedimientos
+remotos adicionales, etc.
+
+Después de completar los ejercicios de este capítulo, hemos creado una
+plataforma sincrónica. En otras palabras, cuando el servicio solicita un objeto
+Active Remote específico, espera una respuesta rápida de uno de los servicios
+poseedores del modelo Active Record. En el próximo capítulo, discutiremos el
+patrón de arquitectura basado en eventos. Este patrón nos permite agregar uno o
+más servicios, cada uno de los cuales puede realizar una acción cuando se
+detecta un evento.
+
+[Siguiente >>](110-chapter-10.es.md)
+
+ [Rubygems]: https://rubygems.org.
diff --git a/100-chapter-09.md b/100-chapter-09.md
index c9dcb8a..e431ef4 100644
--- a/100-chapter-09.md
+++ b/100-chapter-09.md
@@ -174,7 +174,7 @@ service EmployeeMessageService {
rpc Search (EmployeeMessageRequest) returns (EmployeeMessageList);
rpc Create (EmployeeMessage) returns (EmployeeMessage);
rpc Update (EmployeeMessage) returns (EmployeeMessage);
- rpc Destroy (EmployeeMessage) returns (EmployeeMessage);
+ rpc Destroy (EmployeeMessage) returns (EmployeeMessage);
}
```
@@ -185,7 +185,7 @@ _**Listing 9-6**_ Rakefile
```ruby
# rails-microservices-sample-code/protobuf/Rakefile
-require "protobuf/tasks"
+require 'protobuf/tasks'
```
Now we can run the `compile` Rake task to generate the file.
@@ -239,9 +239,9 @@ require 'protobuf'
class Employee < ApplicationRecord
protobuf_message :employee_message
- scope :by_guid, lambda { |*values| where(guid: values) }
- scope :by_first_name, lambda { |*values| where(first_name: values) }
- scope :by_last_name, lambda { |*values| where(last_name: values) }
+ scope :by_guid, ->(*values) { where(guid: values) }
+ scope :by_first_name, ->(*values) { where(first_name: values) }
+ scope :by_last_name, ->(*values) { where(last_name: values) }
field_scope :guid
field_scope :first_name
@@ -262,7 +262,7 @@ class EmployeeMessageService
def search
records = ::Employee.search_scope(request).map(&:to_proto)
- respond_with records: records
+ respond_with records:
end
def create
@@ -411,17 +411,17 @@ _**Listing 9-14**_ Employee controller class
@employees = Employee.search({})
end
- ...
+...
- def new
- @employee = Employee.new(guid: SecureRandom.uuid)
- end
+def new
+ @employee = Employee.new(guid: SecureRandom.uuid)
+end
- ...
+...
- def set_employee
- @employee = Employee.search(guid: params[:id]).first
- end
+def set_employee
+ @employee = Employee.search(guid: params[:id]).first
+end
```
### Create and Configure Our Environment
diff --git a/110-chapter-10.es.md b/110-chapter-10.es.md
new file mode 100644
index 0000000..56414e7
--- /dev/null
+++ b/110-chapter-10.es.md
@@ -0,0 +1,71 @@
+### Chapter 10 - Event Driven Messaging
+
+## Introducción
+
+Hasta este punto, hemos creado una plataforma que se comunica entre servicios de
+forma sincrónica. Un sistema de mensajes se encuentra comunicando las
+aplicaciones, pero las aplicaciones emiten solicitudes y esperan respuestas
+inmediatas. Este patrón funciona muy bien para recuperar y modificar datos. ¿Qué
+pasa si deseáramos notificar a los servicios cuando sucede algo en otro servicio
+? Nos adentramos en la arquitectura basada en eventos.
+
+Una arquitectura basada en eventos se puede mostrar con el siguiente ejemplo:
+imaginemos dos servicios, uno es un sistema de recursos humanos y otro es un
+sistema de nómina. En un sistema sincrónico, se podría crear un nuevo registro
+de empleado en el registro de los recursos humanos y luego, siguiendo el flujo
+de trabajo sincrónico (un callback a Active Model, una llamada desde un service
+object, etc.), se podría realizar una llamada para pasar información al sistema
+de nómina para crear un nuevo registro de nómina de empleado. Una ventaja de
+crear software de esta manera es que el proceso es sincrónico y los errores se
+pueden informar inmediatamente al usuario. Una desventaja es que estos dos
+servicios están estrechamente acoplados. Si el servicio de nómina estuviera
+desconectado por algún motivo, el servicio de recursos humanos parecería tener
+problemas y el usuario podría percibir que todo el sistema fall y podría
+proporcionar información incorrecta al informar sobre el problema.
+
+Hay una serie de ventajas al construir sobre esta arquitectura. Una es que los
+servicios pueden tener un acoplamiento flexible. Si los servicios se
+construyeran sobre una arquitectura basada en eventos y el sistema de recursos
+humanos estuviera en línea, el usuario podría agregar nuevos empleados y la
+nómina (u otros servicios que vigilan los eventos creados por los mismos
+empleados) realizarían su procesamiento en segundo plano. La experiencia
+percibida por el usuario sería mejor porque el usuario interactúa con un pequeño
+servicio que realiza pocas tareas; debido a que tiene menos responsabilidad,
+podríamos esperar que sea una aplicación con mayor capacidad de respuesta. Lo
+que sucede en segundo plano no es asunto del usuario ni está obligado a esperar
+a que todos los demás servicios completen el procesamiento antes de devolver el
+control al usuario.
+
+Otra ventaja del procesamiento asincrónico en una plataforma basada en eventos
+es que se pueden agregar servicios adicionales para observar los mismos eventos,
+como el evento creado por los empleados mencionado anteriormente. Por ejemplo,
+si más adelante se decide que todos los empleados nuevos reciban una tarjeta de
+regalo para cenar semanalmente, se podría agregar un nuevo servicio a la
+plataforma que observe el evento creado por el empleado. Agregar este nuevo
+servicio para observar un evento existente no requeriría ninguna configuración
+ni tiempo de inactividad para los servicios existentes.
+
+## Implementación
+
+El software que proporciona mensajes basados en eventos a veces se denomina
+intermediario (broker). Algunos de los brokers más populares incluyen, entre
+otros: Apache Kafka, RabbitMQ y Amazon Simple Queue Service (Amazon SQS).
+
+Las gemas que usaremos para implementar nuestra arquitectura basada en eventos
+en los próximos capítulos están diseñadas en torno a RabbitMQ, por lo que a
+partir de aquí nos centraremos en las funciones proporcionadas por el agente de
+mensajes RabbitMQ.
+
+## Recapitulando
+
+Las arquitecturas de mensajes impulsadas por eventos ofrecen numerosas ventajas.
+Algunas de estas ventajas son la falta de conexión de servicios, cero tiempo de
+inactividad al agregar nuevos servicios y, en algunos casos, una mejor
+experiencia de usuario.
+
+Anteriormente, implementamos el servicio NATS para enrutar mensajes entre
+nuestros servicios. En el próximo capítulo, pondremos en marcha un servicio
+RabbitMQ, nos suscribiremos y publicaremos eventos para ver cómo funciona un
+broker de mensajes.
+
+[Siguiente >>](120-chapter-11.es.md)
diff --git a/120-chapter-11.es.md b/120-chapter-11.es.md
new file mode 100644
index 0000000..abd37c4
--- /dev/null
+++ b/120-chapter-11.es.md
@@ -0,0 +1,150 @@
+### Capítulo 11 - Sistemas de Mensajes - Rabbit MQ
+
+## Introducción
+
+RabbitMQ es una de las muchas implementaciones de intermediarios de mensajes.
+Proporciona muchas funciones, como cola de mensajes, compatibilidad con
+múltiples protocolos de mensajes y acuse de recibo de entrega. Discutiremos
+algunas de estas características en este capítulo.
+
+Ejecutaremos RabbitMQ de la misma manera que ejecutamos NATS en el capítulo 6:
+Docker lo hace fácil. Seguiremos la mayoría de los mismos pasos pero con un
+archivo docker-compose.yml ligeramente diferente.
+
+## Ejecutémoslo
+
+Ejecutemos un servidor RabbitMQ local y enviémosle mensajes. Incluiremos una
+imagen de Ruby para que podamos crear un cliente Ruby para enviar y recibir
+mensajes de colas en el servidor RabbitMQ. Con NATS usamos telnet para enviar y
+recibir mensajes simples de pruebas; con RabbitMQ necesitaremos construir
+mensajes binarios que enviaremos a RabbitMQ.
+
+_**Paso #1**_ Fichero de Docker Compose con RabbitMQ y Ruby
+
+```yml
+# ~/projects/rabbitmq/docker-compose.yml
+# usage: docker-compose up
+
+version: "3.4"
+
+services:
+ rabbit:
+ image: rabbitmq:latest
+ ports:
+ - 5672:5672
+ stdin_open: true
+ ruby:
+ image: ruby:2.6.5
+ stdin_open: true
+```
+
+Cree un archivo con el nombre `docker-compose.yml` e incluya el código anterior
+como su contenido. Ahora cambiemos a ese directorio y ejecutemos los
+contenedores. Las versiones del software y la salida pueden diferir de lo que ve
+en su terminal.
+
+_**Paso #2**_ Ejecutar RabbitMQ y Ruby
+
+```console
+$ cd ~/projects/rabbitmq
+$ docker-compose up
+Starting rabbitmq_ruby_1 ... done
+Creating rabbitmq_rabbit_1 ... done
+Attaching to rabbitmq_ruby_1, rabbitmq_rabbit_1
+ruby_1 | Switch to inspect mode.
+rabbit_1 | Starting RabbitMQ 3.8.2 on Erlang 22.2.3
+...
+rabbit_1 | ## ## RabbitMQ 3.8.2
+rabbit_1 | ## ##
+rabbit_1 | ########## Copyright (c) 2007-2019 Pivotal Software, Inc.
+rabbit_1 | ###### ##
+rabbit_1 | ########## Licensed under the MPL 1.1. Website: https://rabbitmq.com
+...
+rabbit_1 | Starting broker...2020-01-25 14:18:45.535 [info] <0.267.0>
+...
+rabbit_1 | 2020-01-25 14:18:46.099 [info] <0.8.0> Server startup complete; 0 plugins started.
+```
+
+Ahora vamos a utilizar el patrón productor/comsumidor para una mayor y mejor
+comprensión. Crear un productor es simple: nos conectaremos al contenedor Ruby y
+ejecutaremos IRB para crear una conexión, un canal y una cola. Crearemos un
+mensaje en una nueva ventana de terminal.
+
+_**Paso #3**_ Crear un mensage
+
+```console
+$ docker-compose exec ruby bash
+/# gem install bunny # install the bunny gem
+...
+/# irb
+irb(main):001:0> require 'bunny'
+irb(main):002:0> connection = Bunny.new(hostname: 'rabbit') # the 'rabbit' hostname was defined as the service name in the docker-compose.yml file
+irb(main):003:0> connection.start
+irb(main):004:0> channel = connection.create_channel # create a new channel
+irb(main):005:0> queue = channel.queue('hello') # create a queue
+irb(main):006:0> channel.default_exchange.publish('Hello World', routing_key: queue.name) # encode a string to a byte array and publish the message to the 'hello' queue
+```
+
+Crear un consumidor es casi tan simple como crear un productor. Crearemos una
+conexión, un canal y una cola. El comando `queue` se suscribirá a colas con el
+mismo nombre o, si no existe, creará una nueva cola.
+
+Abramos otra ventana de terminal y ejecutemos los comandos listados en el paso
+#4.
+
+_**Paso #4**_ Consumir mensajes
+
+```console
+$ docker-compose exec ruby bash
+/# irb
+irb(main):001:0> require 'bunny'
+irb(main):002:0> connection = Bunny.new(hostname: 'rabbit') # the 'rabbit' hostname was defined as the service name in the docker-compose.yml file
+irb(main):003:0> connection.start
+irb(main):004:0> channel = connection.create_channel # create a new channel
+irb(main):005:0> queue = channel.queue('hello') # create a queue (or connect to an existing queue if it already exists)
+irb(main):006:0> queue.subscribe(block: true) do |_delivery_info, _properties, body|
+irb(main):007:1* puts "- Received #{body}"
+irb(main):008:0> end
+- Received Hello World
+```
+
+Deberíamos ver el mensaje "- Received Hello World" en la ventana de terminal
+donde consumimos el mensaje (Listado 11-4). Esto demuestra que tenemos el
+servidor RabbitMQ ejecutándose, publicamos un mensaje en una cola y nuestro
+consumidor recibió el mensaje. Volvamos a la sesión IRB que comenzamos en el
+paso #3 y publiquemos otro mensaje.
+
+_**Paso #5**_ Publicar un segundo mensaje
+
+```console
+irb(main):007:0> channel.default_exchange.publish('We thought you were a toad!', routing_key: queue.name) # encode and publish another message
+irb(main):008:0> connection.close # the message has been sent, so let's close the connection
+```
+
+Si regresamos a la terminal en el paso #4 donde creamos un consumidor,
+deberíamos ver tanto '- Received Hello World' como '- Received We thought you
+were a toad!'. ¡Felicidades! Hemos iniciado con éxito un servidor RabbitMQ y un
+par de clientes Ruby para publicar y consumir mensajes.
+
+Cuando hayamos terminado, usemos `Ctrl-C` para salir de la terminal de
+consumidor RabbitMQ y luego `Ctrl-D` o escribir `exit` para regresar al símbolo
+del sistema de la máquina host.
+
+## Recursos
+
+* https://www.rabbitmq.com/tutorials
+
+## Recapitulando
+
+Los sistemas de mensajes basados en eventos son una capa en la arquitectura
+del sistema que le permite construir una plataforma asincrónica, confiable,
+desacoplada y escalable. Junto con NATS, RabbitMQ es uno de esos sistemas de
+mensajes fácil de configurar y usar.
+
+En este capítulo, pusimos en marcha con éxito un servidor RabbitMQ local,
+creamos una cola y luego publicamos y consumimos mensajes en esa cola.
+
+En el próximo capítulo analizaremos un par de gemas de Ruby que facilitan la
+configuración y puesta en marcha de nuestros clientes RabbitMQ.
+
+[Siguiente >>](130-chapter-12.es.md)
diff --git a/130-chapter-12.es.md b/130-chapter-12.es.md
new file mode 100644
index 0000000..8417ee2
--- /dev/null
+++ b/130-chapter-12.es.md
@@ -0,0 +1,729 @@
+### Capítulo 12 - El Sandbox de Mensajes Orientados a Eventos
+
+> ¡Te voy a dar una patada por el culo, mongólico, hijo de la gran puta!
+> No le dices a papá lo que tiene que hacer. Aquí no estamos cada uno por
+> su lado. ¡Estamos comunicándonos MASIVAMENTE!
+>
+> Pappy O'Daniel, O Brother Where Art Thou?
+
+## Introducción
+
+En el capítulo 9, configuramos un entorno sandbox para experimentar con el
+envío de mensajes Protobuf a través de una cola NATS. En este capítulo,
+usaremos Docker y Docker Compose para crear un entorno y analizar exactamente
+qué dependencias necesitaremos para que un publicador (traducción literal del
+término en inglés _«publisher»_) y un consumidor estén en funcionamiento. El
+patrón de arquitectura _«pub-sub»_ (por sus siglas en Inglés) nos permite
+publicar eventos para 0 o más suscriptores, sin saber nada sobre los
+destinatarios individuales.
+
+En este entorno sandbox, crearemos un publicador y un único suscriptor. No estamos
+limitados a un solo suscriptor, como lo ilustra la Listado 12-1. Usaremos el
+patrón de disparar y olvidar (_«fire and forget»_) para publicar un mensaje y
+se notificará a todas las partes interesadas.
+
+
+
+_**Listado 12-1**_ Dispara y olvida
+
+Para este entorno sandbox, crearemos un único publicador y un único suscriptor.
+
+## Lo que necesitaremos
+
+* Gemas de ruby
+ * Publicador activo
+ * Action Subscriber
+ * Protobuf
+ * Rails
+* RabbitMQ
+* Ruby
+* SQLite
+
+Active Publisher es una gema que facilita la configuración de un publicador
+RabbitMQ en una aplicación Rails. Depende de la gema bunny que usamos
+para probar en el capítulo 11.
+
+Action Subscriber es otra gema que usaremos y que facilita la configuración de
+un consumidor RabbitMQ en una aplicación Rails. Action Subscriber también
+proporciona un lenguaje específico de dominio (_«DSL»_) que facilita la
+definición y suscripción a colas en el servidor RabbitMQ.
+
+Usaremos la gema Protobuf para codificar y decodificar nuestros datos como se
+describe en el capítulo 8.
+
+## Implementación
+
+### Estructura del directorio del proyecto
+
+Vamos a crear un directorio para nuestro proyecto. Necesitaremos tres
+subdirectorios del proyecto, uno para nuestros mensajes Protobuf compartidos,
+uno para nuestra aplicación Ruby on Rails de ActivePublisher que usaremos
+para publicar mensajes, y un consumidor. Puede crear múltiples consumidores
+para demostrar que varios clientes pueden escuchar los mismos eventos
+publicados sobre la misma cola, si así lo desea.
+
+En el capítulo 9, creamos un directorio `rails-microservices-sample-code` en
+nuestro directorio principal. La ruta específica no es importante, pero si ha
+estado siguiendo, podemos reutilizar algo del código que generamos en el
+capítulo 9. Siguiendo el tutorial a continuación, deberíamos terminar con los
+siguientes directorios (y muchos archivos y directorios en cada uno).
+
+* rails-microservices-sample-code
+ * chapter-12
+ * active-publisher
+ * action-subscriber
+ * protobuf
+
+### Configurar un Entorno de Desarrollo
+
+Algunos de los pasos a continuación son los mismos que los cubiertos en el
+capítulo 9. Reutilizaremos algunos de los mismos Dockerfiles lo cual mantendrá
+nuestras versiones de Ruby consistentes. Los incluiremos aquí, para que no
+tengamos que saltar de un capítulo a otro. Si seguimos el capítulo 9 y creamos
+estos archivos, podemos saltarnos algunos de estos pasos.
+
+Vamos a crear un builder (constructor) Dockerfile y un archivo Docker Compose.
+Usaremos el archivo Dockerfile para construir una imagen con las aplicaciones
+de línea de comandos que necesitamos, y usaremos un archivo de configuración de
+Docker Compose para reducir la cantidad de parámetros que necesitaremos usar
+para ejecutar cada comando.
+
+Crea el siguiente archivo Dockerfile en el directorio
+`rails-microservices-sample-code`. Usaremos el nombre `Dockerfile.builder` para
+diferenciar el Dockerfile que usaremos para generar nuevos servicios de Rails
+versus el Dockerfile que usaremos para construir y ejecutar nuestras
+aplicaciones Rails.
+
+_**Listado 12-1**_ Dockerfile usado para crear una imagen que usaremos para
+generar nuestra aplicación Rails.
+
+```dockerfile
+# rails-microservices-sample-code/Dockerfile.builder
+
+FROM ruby:2.6.5
+
+RUN apt-get update && apt-get install -qq -y --no-install-recommends \
+ build-essential \
+ protobuf-compiler \
+ nodejs \
+ vim
+
+WORKDIR /home/root
+
+RUN gem install rails -v 5.2.4
+RUN gem install protobuf
+```
+
+Crearemos el siguiente archivo `docker-compose.builder.yml` en el directorio
+`rails-microservices-sample-code`. Utilizaremos este archivo de configuración
+para iniciar nuestro entorno de desarrollo con todas las herramientas de línea
+de comandos que necesitaremos.
+
+_**Listado 12-2**_ Archivo de Docker Compose para iniciar el contenedor que
+usaremos para generar nuestra aplicación Rails.
+
+```yaml
+# rails-microservices-sample-code/docker-compose.builder.yml
+
+version: "3.4"
+
+services:
+ builder:
+ build:
+ context: .
+ dockerfile: Dockerfile.builder
+ volumes:
+ - .:/home/root
+ stdin_open: true
+ tty: true
+```
+
+Vamos a iniciar y conectarnos al contenedor builder. Luego ejecutaremos
+los comandos de generación de Rails desde el contenedor, lo que creará dos
+aplicaciones Rails. Debido a que hemos mapeado un volumen en el archivo `.yml`
+anterior, los archivos que se generen se guardarán en el directorio
+`rails-microservices-sample-code`. Si no mapeáramos un volumen, los archivos
+que generamos solo existirían dentro del contenedor, y cada vez que
+detuviéramos y reiniciáramos el contenedor, tendrían que ser regenerados.
+Mapear un volumen a un directorio en la computadora principal hará que los
+archivos estén disponibles a través del entorno del contenedor, que incluye una
+versión específica de Ruby, Rails y las gemas que necesitaremos para ejecutar
+nuestras aplicaciones.
+
+_**Listado 12-3**_ Iniciando nuestro contenedor del constructor
+
+```console
+$ docker-compose -f docker-compose.builder.yml run builder bash
+```
+
+El comando `run` de Docker Compose construirá la imagen (si aún no se ha
+construido), iniciará el contenedor, se conectará al contenedor en ejecución y
+nos proporcionará un símbolo del sistema utilizando el shell `bash`.
+
+Ahora deberíamos ver que hemos iniciado sesión como usuario root en el
+contenedor (veremos un símbolo del sistema que comienza con un signo de
+almohadilla `#`). Iniciar sesión como usuario root suele ser aceptable dentro
+de un contenedor, porque el aislamiento del entorno del contenedor limita lo
+que el usuario root puede hacer.
+
+### Protobuf
+
+Ahora crearemos un mensaje Protobuf y compilaremos el archivo `.proto` para
+generar el archivo Ruby relacionado, que contendrá las clases que se copiarán
+en cada una de nuestras aplicaciones Ruby on Rails. Este archivo definirá el
+mensaje Protobuf, las solicitudes y las definiciones de llamadas a
+procedimientos remotos.
+
+Crearemos un par de directorios para nuestros archivos de entrada y salida. El
+comando `mkdir -p` a continuación creará directorios con la siguiente
+estructura:
+
+* protobuf
+ * definitions
+ * lib
+
+_**Listado 12-4**_ Creando los directorios necesarios
+
+```console
+$ mkdir -p protobuf/{definitions,lib}
+```
+
+El fichero de definición Protobuf:
+
+_**Listado 12-5**_ Fichero Protobuf del Mensaje Employee
+
+```protobuf
+# rails-microservices-sample-code/protobuf/definitions/employee_message.proto
+
+syntax = "proto3";
+
+message EmployeeMessage {
+ string guid = 1;
+ string first_name = 2;
+ string last_name = 3;
+}
+
+message EmployeeMessageRequest {
+ string guid = 1;
+ string first_name = 2;
+ string last_name = 3;
+}
+
+message EmployeeMessageList {
+ repeated EmployeeMessage records = 1;
+}
+
+```
+
+El servicio EmployeeMessageService se utilizó para ActiveRemote en el capítulo 9
+, pero no es necesario aquí. Si ya tenemos este servicio definido, podemos
+dejarlo así si lo deseamos.
+
+
+Para compilar los archivos `.proto`, utilizaremos una tarea Rake proporcionada
+por la gema `protobuf`. Para acceder a las tareas Rake de la gema `protobuf`,
+necesitaremos crear un `Rakefile`. Hagámoslo ahora.
+
+_**Listing 12-6**_ Rakefile
+
+```ruby
+# rails-microservices-sample-code/protobuf/Rakefile
+
+require 'protobuf/tasks'
+```
+
+Ahora podemos ejecutar la tarea Rake `compile` para generar el archivo.
+
+_**Listado 12-7**_ Iniciando el contenedor del constructor y compilando la
+definición de protobuf.
+
+```console
+$ docker-compose -f docker-compose.builder.yml run builder bash
+# cd protobuf
+# rake protobuf:compile
+```
+
+Esto generará un archivo llamado `employee_message.pb.rb` en el directorio
+`protobuf/lib`. Copiaremos este archivo al directorio `app/lib` en las
+aplicaciones Rails que crearemos a continuación.
+
+### Crear un Publicador de Mensajes en Rails
+
+La primera aplicación Rails que generaremos utilizará la gema ActivePublisher
+para publicar mensajes en RabbitMQ. Añadiremos la gema `active_publisher` al
+archivo `Gemfile`. Luego ejecutaremos el comando `bundle` para obtener las
+gemas desde https://rubygems.org. Después de obtener las gemas, crearemos el
+andamiaje para una entidad Employee. Esta aplicación almacenará los datos en
+una base de datos SQLite para que podamos experimentar con los eventos de
+creación y actualización.
+
+Generemos la aplicación Rails que actuará como el publicador de los eventos.
+Llamaremos a esta aplicación `active-publisher`. También añadiremos la gema
+Protobuf Active Record para que podamos serializar nuestro objeto Active Record
+a un mensaje Protobuf.
+
+_**Listado 12-8**_ Generando las aplicaciones Rails y los archivos necesarios
+
+```console
+$ mkdir chapter-12 # create a directory for this chapter
+$ docker-compose -f docker-compose.builder.yml run builder bash
+# cd chapter-12
+# rails new active-publisher
+# cd active-publisher
+# echo "gem 'active_publisher'" >> Gemfile
+# echo "gem 'protobuf-activerecord'" >> Gemfile
+# bundle
+# rails generate scaffold Employee guid:string first_name:string last_name:string
+# rails db:migrate
+# exit
+```
+
+Asegúrate de inspeccionar la salida de cada uno de los comandos anteriores,
+buscando errores. Si se encuentran errores, por favor verifica cada comando en
+busca de errores tipográficos o caracteres adicionales.
+
+Personalicemos la aplicación para servir nuestra entidad Employee a través de
+Protobuf. Necesitaremos un directorio `app/lib`, y luego copiaremos el archivo
+`employee_message.pb.rb` generado a este directorio.
+
+_**Listado 12-9**_ Configurando el directorio app/lib
+
+```console
+$ mkdir chapter-12/active-publisher/app/lib
+$ cp protobuf/lib/employee_message.pb.rb chapter-12/active-publisher/app/lib/
+```
+
+A continuación, agregaremos un archivo de configuración `active_publisher` al
+directorio `config`. Este archivo definirá cómo nuestra aplicación debería
+conectarse al servidor RabbitMQ. El host `rabbit` se definirá en el archivo
+`docker-compose` que definiremos en un par de minutos.
+
+_**Listado 12-10**_ Configuración de ActivePublisher
+
+```yml
+# rails-microservices-sample-code/chapter-12/active-publisher/config/active_publisher.yml
+
+default: &default
+ host: rabbit
+ username: guest
+ password: guest
+
+development:
+ <<: *default
+```
+
+Ahora crearemos un inicializador para Active Publisher. Esto cargará la gema,
+establecerá el adaptador y cargará el archivo de configuración. Lo haremos en el
+directorio `config/initializers`.
+
+_**Listado 12-11**_ Inicializador de Active Publisher
+
+```ruby
+# rails-microservices-sample-code/chapter-12/active-publisher/config/initializers/active_publisher.rb
+
+require 'active_publisher'
+
+::ActivePublisher::Configuration.configure_from_yaml_and_cli
+```
+
+A continuación, modificaremos el modelo de Employee para que podamos enviar el
+objeto de Employee Protobuf a RabbitMQ. Utilizaremos los callbacks de Active
+Record para publicar mensajes en colas separadas de `created` y `updated`
+después de que se haya creado o modificado un registro de Employee. Abre el
+archivo `app/models/employee.rb` y agrega el siguiente código.
+
+_**Listing 12-12**_ Modelo Employee de Active Record
+
+```ruby
+# rails-microservices-sample-code/chapter-12/active-publisher/app/models/employee.rb
+
+require 'protobuf'
+
+class Employee < ApplicationRecord
+ protobuf_message :employee_message
+
+ after_create :publish_created
+ after_update :publish_updated
+
+ def publish_created
+ Rails.logger.info "Publishing employee object #{inspect} on the employee.created queue."
+ ::ActivePublisher.publish('employee.created', to_proto.encode, 'events', {})
+ end
+
+ def publish_updated
+ Rails.logger.info "Publishing employee object #{inspect} on the employee.updated queue."
+ ::ActivePublisher.publish('employee.updated', to_proto.encode, 'events', {})
+ end
+end
+```
+
+Dado que estamos utilizando GUIDs para identificar de manera única los objetos
+que estamos serializando y pasando entre servicios, modificaremos la acción
+`new` del controlador para que genere un nuevo GUID.
+
+_**Listado 12-13**_ Controlador de empleado
+
+```ruby
+# rails-microservices-sample-code/chapter-12/active-publisher/controllers/employees_controller.rb
+
+def new
+ @employee = Employee.new(guid: SecureRandom.uuid)
+end
+```
+
+También necesitaremos agregar algunos detalles adicionales. Debido a que el
+archivo `app/lib/employee_message.pb.rb` contiene varias clases, solo se carga
+la clase que coincide con el nombre del archivo. En modo de desarrollo, Rails
+puede cargar archivos de forma retardada (_«lazy load»_) siempre que el nombre
+del archivo se pueda inferir a partir del nombre de la clase, por ejemplo, el
+código que requiere la clase `EmployeeMessageService` intentará cargar
+de manera retardada un archivo llamado `employee_message_service.rb`, y lanzará
+un error si no se encuentra el archivo. Podemos separar las clases en el
+archivo `app/lib/employee_message.pb.rb` en archivos separados, o habilitar la
+carga ansiosa (_«eager load»_) en la configuración. En esta demostración
+habilitaremos la carga ansiosa y también almacenaremos en caché las clases.
+También necesitaremos configurar el registro para enviar la salida a los
+registros de Docker.
+
+_**Listado 12-14**_ Configuración de desarrollo
+
+```ruby
+# rails-microservices-sample-code/chapter-12/active-publisher/config/environments/development.rb
+
+Rails.application.configure do
+ ...
+ config.cache_classes = true
+ ...
+ config.eager_load = true
+ ...
+ logger = ActiveSupport::Logger.new($stdout)
+ logger.formatter = config.log_formatter
+ config.logger = ActiveSupport::TaggedLogging.new(logger)
+end
+```
+
+Eso es todo. Ahora construyamos nuestro suscriptor.
+
+### Crear un Suscriptor de Mensajes
+
+Crearemos la aplicación `action-subscriber` la cual se suscribirá a las colas de
+mensajes `created` y `updated` de Employee y simplemente registrará que
+recibió un mensaje en la cola.
+
+_**Listado 12-15**_ Generando las aplicaciones Rails y los archivos necesarios
+
+```console
+$ docker-compose -f docker-compose.builder.yml run builder bash
+# cd chapter-12
+# rails new action-subscriber --skip-active-record
+# cd action-subscriber
+# echo "gem 'action_subscriber'" >> Gemfile
+# echo "gem 'protobuf'" >> Gemfile
+# bundle
+# exit
+```
+
+Ahora configuraremos Action Subscriber para escuchar eventos. Necesitaremos
+agregar una clase `EmployeeSubscriber` y añadir rutas a través del método
+`ActionSubscriber.draw_routes`.
+
+Colocaremos nuestras clases suscriptoras en su propio directorio `subscribers`.
+También necesitaremos el directorio `lib` donde copiaremos nuestra clase
+Protobuf de Employee. Crearemos estos directorios y copiaremos los archivos a
+uno de esos directorios:
+
+_**Listado 12-16**_ Generando directorios de la aplicación Rails y copiando la
+clase de mensaje
+
+```console
+$ mkdir chapter-12/action-subscriber/app/{lib,subscribers}
+$ cp protobuf/lib/employee_message.pb.rb chapter-12/action-subscriber/app/lib/
+```
+
+Ahora agregaremos la clase suscriptora y para mantenerla simple: solo
+registraremos que recibimos el mensaje.
+
+_**Listado 12-17**_ Clase suscriptora de Employee
+
+```ruby
+# rails-microservices-sample-code/chapter-12/action-subscriber/app/subscribers/employee_subscriber.rb
+
+class EmployeeSubscriber < ::ActionSubscriber::Base
+ def created
+ Rails.logger.info "Received created message: #{EmployeeMessage.decode(payload).inspect}"
+ end
+
+ def updated
+ Rails.logger.info "Received updated message: #{EmployeeMessage.decode(payload).inspect}"
+ end
+end
+```
+
+Nuestra aplicación necesita saber a qué colas suscribirse, por lo que usamos el
+método `default_routes_for`, que leerá nuestra clase `EmployeeSubscriber` y
+generará colas para cada uno de nuestros métodos públicos o se suscribirá a
+esas colas si ya existen. El nombre de host `host.docker.internal` es un nombre
+de host especial de Docker, que apunta a la dirección IP de la máquina
+anfitriona.
+
+_**Listado 12-18**_ Inicializador de Action Subscriber
+
+```ruby
+# rails-microservices-sample-code/chapter-12/action-subscriber/config/initializers/action_subscriber.rb
+
+ActionSubscriber.draw_routes do
+ default_routes_for EmployeeSubscriber
+end
+
+ActionSubscriber.configure do |config|
+ config.hosts = ['host.docker.internal']
+ config.port = 5672
+end
+```
+
+Necesitaremos habilitar las configuraciones `cache_classes` y `eager_load`, de
+la misma manera que lo hicimos para el publicador. También necesitaremos
+configurar un registrador (_«logger»_) para poder ver la salida del registro desde nuestro
+contenedor Docker.
+
+_**Listado 12-19**_ Configuración de desarrollo
+
+```ruby
+# rails-microservices-sample-code/chapter-12/action-subscriber/config/environments/development.rb
+
+config.cache_classes = true
+...
+config.eager_load = true
+...
+logger = ActiveSupport::Logger.new($stdout)
+logger.formatter = config.log_formatter
+config.logger = ActiveSupport::TaggedLogging.new(logger)
+```
+
+### Crear y Configurar Nuestro Entorno
+
+Por último, pero no menos importante, agregaremos archivos `Dockerfile` y
+`docker-compose.yml` para construir una imagen y poner en marcha nuestros
+contenedores de Rails y RabbitMQ. Es posible que el `Dockerfile` ya exista
+desde el sandbox que construimos en el capítulo 9, pero si no es así, tiene el
+mismo contenido aquí. El archivo `docker-compose.yml` es nuevo.
+
+_**Listado 12-20**_ Dockerfile del Sandbox
+
+```dockerfile
+# rails-microservices-sample-code/Dockerfile
+
+FROM ruby:2.6.5
+
+RUN apt-get update && apt-get install -qq -y --no-install-recommends build-essential nodejs
+
+ENV INSTALL_PATH /usr/src/service
+ENV HOME=$INSTALL_PATH PATH=$INSTALL_PATH/bin:$PATH
+RUN mkdir -p $INSTALL_PATH
+WORKDIR $INSTALL_PATH
+
+RUN gem install rails -v 5.2.4
+
+ADD Gemfile* ./
+RUN set -ex && bundle install --no-deployment
+```
+
+El siguiente archivo Docker Compose incluye una instancia de RabbitMQ y
+nuestras nuevas aplicaciones Rails `active-publisher` y `action-subscriber`.
+Expondremos la aplicación web en el puerto 3001. RabbitMQ puede tardar unos
+segundos en iniciarse, así que configuraremos nuestro servicio
+`action-subscriber` para que se reinicie si no puede conectarse. En una
+aplicación del mundo real, querremos verificar la respuesta de RabbitMQ antes
+de iniciar el suscriptor.
+
+Normalmente, agregaríamos el suscriptor al mismo archivo Docker Compose, pero
+como el servicio Action Subscriber intenta conectarse inmediatamente y RabbitMQ
+puede tardar unos segundos en cargarse, ejecutaremos el proceso de suscriptor
+desde un archivo Docker Compose separado. También necesitaremos exponer el
+puerto 5672 a la máquina host para que podamos conectar desde otro entorno de
+orquestación (_«Compose environment»_).
+
+_**Listado 12-21**_ Archivo Docker Compose del Sandbox
+
+```yml
+# rails-microservices-sample-code/chapter-12/docker-compose.yml
+# Usage: docker-compose up
+
+version: "3.4"
+
+services:
+ active-publisher:
+ build:
+ context: ./active-publisher
+ dockerfile: ../../Dockerfile
+ command: bundle exec puma -C config/puma.rb
+ volumes:
+ - ./active-publisher:/usr/src/service
+ ports:
+ - 3001:3000
+ depends_on:
+ - rabbit
+ rabbit:
+ image: rabbitmq:latest
+ ports:
+ - 5672:5672
+```
+
+Ahora agregaremos el archivo de configuración `action-subscriber`. Teniendo en
+cuenta que debido a que el ejecutable Action Subscriber genera un proceso
+secundario para escuchar eventos desde RabbitMQ, perdemos la salida de registro
+si iniciamos el contenedor usando el comando `up`. Para ver toda la información
+de registro en la terminal, usaremos el comando `run` de Docker Compose para
+iniciar un shell bash y ejecutar nuestro ejecutable `action_subscriber` allí.
+
+_**Listado 12-22**_ Archivo Docker Compose del suscriptor del Sandbox
+
+```yml
+# rails-microservices-sample-code/chapter-12/docker-compose-subscriber.yml
+# Usage: docker-compose -f docker-compose-subscriber.yml run action-subscriber bash -c 'bundle exec action_subscriber start'
+
+version: "3.4"
+
+services:
+ action-subscriber:
+ build:
+ context: ./action-subscriber
+ dockerfile: ../../Dockerfile
+ volumes:
+ - ./action-subscriber:/usr/src/service
+
+```
+
+Ahora que todo está en su lugar, iniciemos nuestro entorno sandbox. Dado que ya
+podríamos tener un archivo `docker-compose.yml` en el directorio, hemos
+nombrado nuestros nuevos archivos de configuración `docker-compose.yml` y
+`docker-compose-subscriber.yml`. Si ejecutáramos la versión más corta del
+comando `docker-compose up`, por defecto buscaría y cargaría el archivo
+`docker-compose.yml`. Podemos usar la opción `-f` para especificar que
+queremos usar otros archivos de configuración en su lugar. Ejecutaremos esos
+comandos ahora.
+
+_**Listado 12-23**_ Iniciando el sandbox
+
+```console
+$ cd chapter-12
+$ docker-compose up
+```
+
+Una vez que veas líneas como estas, RabbitMQ ha iniciado y la aplicación Rails
+Active Publisher se ha conectado correctamente.
+
+_**Listado 12-24**_ Registro del Sandbox
+
+```console
+rabbit_1 | 2020-02-09 22:54:02.253 [info] <0.8.0> Server startup complete; 0 plugins started.
+rabbit_1 | completed with 0 plugins.
+72.30.0.2:5672)
+rabbit_1 | 2020-02-09 22:54:37.395 [info] <0.641.0> connection <0.641.0> (172.30.0.1:53140 -> 172.30.0.2:5672): user 'guest' authenticated and granted access to vhost '/'
+```
+
+Ahora vamos a iniciar el suscriptor en otra ventana de la terminal.
+
+_**Listado 12-25**_ Iniciando el sandbox del suscriptor
+
+```console
+$ docker-compose -f docker-compose-subscriber.yml run action-subscriber bash -c 'bundle exec action_subscriber start'
+```
+
+Deberíamos ver una salida como la siguiente.
+
+_**Listado 12-26**_ Registro del sandbox del suscriptor
+
+```console
+I, [2020-02-09T22:54:53.900735 #1] INFO -- : Loading configuration...
+I, [2020-02-09T22:54:53.902758 #1] INFO -- : Requiring app...
+I, [2020-02-09T22:54:59.308155 #1] INFO -- : Starting server...
+I, [2020-02-09T22:54:59.374240 #1] INFO -- : Rabbit Hosts: ["host.docker.internal"]
+Rabbit Port: 5672
+Threadpool Size: 1
+Low Priority Subscriber: false
+Decoders:
+ --application/json
+ --text/plain
+
+I, [2020-02-09T22:54:59.374419 #1] INFO -- : Middlewares [
+I, [2020-02-09T22:54:59.374488 #1] INFO -- : [ActionSubscriber::Middleware::ErrorHandler, [], nil]
+I, [2020-02-09T22:54:59.374542 #1] INFO -- : [ActionSubscriber::Middleware::Decoder, [], nil]
+I, [2020-02-09T22:54:59.374944 #1] INFO -- : ]
+I, [2020-02-09T22:54:59.375946 #1] INFO -- : EmployeeSubscriber
+I, [2020-02-09T22:54:59.376504 #1] INFO -- : -- method: created
+I, [2020-02-09T22:54:59.376856 #1] INFO -- : -- threadpool: default (1 threads)
+I, [2020-02-09T22:54:59.378129 #1] INFO -- : -- exchange: events
+I, [2020-02-09T22:54:59.379231 #1] INFO -- : -- queue: actionsubscriber.employee.created
+I, [2020-02-09T22:54:59.379911 #1] INFO -- : -- routing_key: employee.created
+I, [2020-02-09T22:54:59.380686 #1] INFO -- : -- prefetch: 2
+I, [2020-02-09T22:54:59.382130 #1] INFO -- : -- method: updated
+I, [2020-02-09T22:54:59.382702 #1] INFO -- : -- threadpool: default (1 threads)
+I, [2020-02-09T22:54:59.383237 #1] INFO -- : -- exchange: events
+I, [2020-02-09T22:54:59.383626 #1] INFO -- : -- queue: actionsubscriber.employee.updated
+I, [2020-02-09T22:54:59.384405 #1] INFO -- : -- routing_key: employee.updated
+I, [2020-02-09T22:54:59.384667 #1] INFO -- : -- prefetch: 2
+I, [2020-02-09T22:54:59.393366 #1] INFO -- : Action Subscriber connected
+```
+
+Estas líneas de registro indican que el suscriptor se ha conectado al servidor
+correctamente, se ha conectado a dos colas y ahora está escuchando eventos.
+
+Crearemos algunos eventos. Vayamos al navegador y carguemos la página
+http://localhost:3001/employees. El puerto 3001 es el puerto que hemos expuesto
+desde la aplicación Rails Active Publisher en el archivo `docker-compose.yml`.
+Deberías ver una página web simple con el título **Empleados** y un enlace
+'Nuevo Empleado'. Continuemos y hagamos clic en el enlace. Ahora deberíamos
+poder crear un nuevo registro de empleado en el formulario web. Una vez que lo
+completemos y hagamos clic en el botón 'Crear Empleado', varias cosas
+sucederán. Primero, los datos del formulario se enviarán de vuelta a la
+aplicación Rails Active Publisher. El controlador pasará esos datos a Active
+Record, que creará un nuevo registro en la base de datos SQLite. A
+continuación, se ejecutará el callback `after_create`, codificando nuestro
+mensaje Protobuf y colocándolo en la cola `actionsubscriber.employee.created`.
+RabbitMQ notificará a los suscriptores de una cola específica de cualquier
+mensaje nuevo. Nuestra aplicación Rails Action Subscriber es uno de esos
+suscriptores. En nuestro método manejador de eventos (_«event handler»_)
+`EmployeeSubscriber#created`, escribimos código para registrar que recibimos un
+mensaje. Si inspeccionas la salida desde la ventana de la terminal donde
+iniciamos la aplicación Rails Action Subscriber, deberías ver una salida como
+la siguiente.
+
+_**Listado 12-27**_ Más registro del sandbox del suscriptor
+
+```console
+I, [2020-02-09T23:14:31.163127 #1] INFO -- : RECEIVED 7a99f6 from actionsubscriber.employee.created
+I, [2020-02-09T23:14:31.163758 #1] INFO -- : START 7a99f6 EmployeeSubscriber#created
+Received created message: #
+I, [2020-02-09T23:14:31.164414 #1] INFO -- : FINISHED 7a99f6
+```
+
+¡Felicidades! Hemos construido con éxito una plataforma de mensajería que puede
+publicar y responder a eventos. Intentemos editar el registro que acabamos de
+crear. Deberíamos ver una salida similar en la aplicación Rails Action
+Subscriber mientras recibe y procesa el evento y los datos. Para aún más
+diversión, intentemos poner en marcha un segundo o tercer servicio suscriptor
+que escuche las mismas colas y observemos cómo todos ellos responden
+simultáneamente al mismo mensaje publicado.
+
+## Recursos
+
+* https://docs.docker.com/compose/reference/run
+* https://github.com/mxenabled/active_publisher
+* https://github.com/ruby-protobuf/protobuf/wiki/Serialization
+
+## Conclusión
+
+En este capítulo, iniciamos un servicio RabbitMQ, construimos una aplicación
+Rails Active Publisher y una aplicación Rails Action Subscriber. Hicimos
+funcionar esos servicios usando dos entornos Docker separados, y publicamos y
+consumimos mensajes. Revisamos los registros donde pudimos ver que los mensajes
+Protobuf se enviaron correctamente de una aplicación Rails a otra.
+
+En el próximo capítulo, vamos a combinar los dos entornos para crear una
+plataforma que pueda recuperar datos de otros servicios según sea necesario.
+¡Pero eso no es todo! También agregaremos escuchas de eventos a nuestros
+servicios para responder a eventos de otros servicios.
+
+[Próximo >>](140-chapter-13.es.md)
diff --git a/130-chapter-12.md b/130-chapter-12.md
index 62efa5c..10c47d7 100644
--- a/130-chapter-12.md
+++ b/130-chapter-12.md
@@ -156,7 +156,7 @@ _**Listing 12-6**_ Rakefile
```ruby
# rails-microservices-sample-code/protobuf/Rakefile
-require "protobuf/tasks"
+require 'protobuf/tasks'
```
Now we can run the `compile` Rake task to generate the file.
@@ -227,7 +227,7 @@ _**Listing 12-11**_ Active Publisher initializer
```ruby
# rails-microservices-sample-code/chapter-12/active-publisher/config/initializers/active_publisher.rb
-require "active_publisher"
+require 'active_publisher'
::ActivePublisher::Configuration.configure_from_yaml_and_cli
```
@@ -248,13 +248,13 @@ class Employee < ApplicationRecord
after_update :publish_updated
def publish_created
- Rails.logger.info "Publishing employee object #{self.inspect} on the employee.created queue."
- ::ActivePublisher.publish("employee.created", self.to_proto.encode, "events", {})
+ Rails.logger.info "Publishing employee object #{inspect} on the employee.created queue."
+ ::ActivePublisher.publish('employee.created', to_proto.encode, 'events', {})
end
def publish_updated
- Rails.logger.info "Publishing employee object #{self.inspect} on the employee.updated queue."
- ::ActivePublisher.publish("employee.updated", self.to_proto.encode, "events", {})
+ Rails.logger.info "Publishing employee object #{inspect} on the employee.updated queue."
+ ::ActivePublisher.publish('employee.updated', to_proto.encode, 'events', {})
end
end
```
@@ -365,7 +365,7 @@ ActionSubscriber.draw_routes do
end
ActionSubscriber.configure do |config|
- config.hosts = ["host.docker.internal"]
+ config.hosts = ['host.docker.internal']
config.port = 5672
end
```
diff --git a/140-chapter-13.es.md b/140-chapter-13.es.md
new file mode 100644
index 0000000..849e786
--- /dev/null
+++ b/140-chapter-13.es.md
@@ -0,0 +1,860 @@
+### Capítulo 13 - Entorno de Eventos con Active Remote
+
+## Introducción
+
+Hasta ahora, hemos construido dos servicios de Rails que pueden comunicarse
+entre sí mediante Active Remote y NATS. También hemos construido dos servicios
+de Rails diferentes que se comunican entre sí mediante Active Publisher, Action
+Subscriber y RabbitMQ. A medida que crecen los requisitos de tu negocio, puedes
+encontrar la necesidad de usar ambos en tu entorno: Active Remote para la
+comunicación en tiempo real entre servicios y Active Subscriber para la
+mensajería basada en eventos.
+
+Afortunadamente, ya hemos sentado las bases para este tipo de plataforma. En
+este capítulo, pondremos en marcha un nuevo entorno sandbox que utiliza tanto
+NATS como RabbitMQ para comunicar y publicar mensajes.
+
+_**Figura 13-1**_ Creando un empleado y notificando a todas las partes
+interesadas
+
+![alt text][]
+
+## Qué Necesitaremos
+
+* NATS
+* RabbitMQ
+* Ruby
+* Gems de Ruby
+ * Active Publisher
+ * Active Remote
+ * Action Subscriber
+ * Protobuf
+ * Rails
+* SQLite
+
+## Implementación
+
+### Estructura del Directorio del Proyecto
+
+Vamos a crear un directorio para nuestro proyecto. Necesitaremos tres
+subdirectorios del proyecto: uno para nuestros mensajes Protobuf compartidos,
+uno para nuestra aplicación Ruby on Rails con Active Publisher que usaremos
+para publicar mensajes y un consumidor. Podrías crear múltiples consumidores
+para demostrar que varios clientes pueden escuchar los mismos eventos
+publicados en la misma cola.
+
+En el capítulo 9, creamos un directorio `rails-microservices-sample-code` en
+nuestro directorio de inicio. La ruta específica no es importante, pero si has
+estado siguiendo, podemos reutilizar parte del código que generamos en el
+capítulo 9. Siguiendo el tutorial en este capítulo, deberías terminar con los
+siguientes directorios (y muchos archivos y directorios en cada directorio).
+
+* rails-microservices-sample-code
+ * chapter-13
+ * active-record
+ * active-remote-publisher
+ * action-subscriber
+ * protobuf
+
+### Configurar un Entorno de Desarrollo
+
+Algunos de los pasos a continuación son los mismos que los pasos cubiertos en
+los capítulos 9 y 12. Reutilizaremos algunos de los mismos Dockerfiles que
+mantendrán nuestras versiones de Ruby consistentes. Los incluiré aquí, solo
+para que no tengamos que saltar de un capítulo a otro. Si seguiste los
+capítulos 9 y 12 y creaste estos archivos, puedes saltarte algunos de estos
+pasos.
+
+Let's create a builder Dockerfile and Docker Compose file. We'll use the
+Dockerfile file to build an image with the command-line apps we need, and we'll
+use a Docker Compose configuration file to reduce the number of parameters
+we'll need to use to run each command.
+
+Crea el siguiente archivo Dockerfile en el directorio
+`rails-microservices-sample-code`. Usaremos el nombre `Dockerfile.builder` para
+diferenciar el Dockerfile que usaremos para generar nuevos servicios de Rails
+del Dockerfile que usaremos para construir y ejecutar nuestras aplicaciones
+Rails.
+
+_**Listado 13-2**_ Dockerfile usado para crear una imagen que utilizaremos para
+generar nuestra aplicación Rails
+
+```dockerfile
+# rails-microservices-sample-code/Dockerfile.builder
+
+FROM ruby:2.6.5
+
+RUN apt-get update && apt-get install -qq -y --no-install-recommends \
+ build-essential \
+ protobuf-compiler \
+ nodejs \
+ vim
+
+WORKDIR /home/root
+
+RUN gem install rails -v 5.2.4
+RUN gem install protobuf
+```
+
+Crea el siguiente archivo `docker-compose.builder.yml` en el directorio
+`rails-microservices-sample-code`. Usaremos este archivo de configuración para
+iniciar nuestro entorno de desarrollo con todas las herramientas de línea de
+comandos que necesitaremos.
+
+_**Listado 13-3**_ Archivo Docker Compose para iniciar el contenedor que
+utilizaremos para generar nuestra aplicación Rails
+
+```yaml
+# rails-microservices-sample-code/docker-compose.builder.yml
+
+version: "3.4"
+
+services:
+ builder:
+ build:
+ context: .
+ dockerfile: Dockerfile.builder
+ volumes:
+ - .:/home/root
+ stdin_open: true
+ tty: true
+```
+
+Vamos a iniciar y acceder al contenedor builder. Luego ejecutaremos los
+comandos de generación de Rails desde el contenedor, lo que creará dos
+aplicaciones Rails. Debido a que hemos asignado un volumen en el archivo `.yml`
+anterior, los archivos que se generen se guardarán en el directorio
+`rails-microservices-sample-code`. Si no asignamos un volumen, los archivos que
+generamos solo existirían dentro del contenedor, y cada vez que detengamos y
+reiniciemos el contenedor tendrían que ser regenerados. Asignar un volumen a un
+directorio en el host permitirá que los archivos se sirvan a través del entorno
+del contenedor, que incluye una versión específica de Ruby, Rails y las gems
+que necesitaremos para ejecutar nuestras aplicaciones.
+
+_**Listado 13-4**_ Iniciando nuestro contenedor builder
+
+```console
+$ docker-compose -f docker-compose.builder.yml run builder bash
+```
+
+El comando `run` de Docker Compose construirá la imagen (si no se ha construido
+ya), iniciará el contenedor, se conectará por ssh al contenedor en ejecución y
+nos dará un prompt de comandos usando el shell `bash`.
+
+Ahora deberías ver que has iniciado sesión como el usuario root en el
+contenedor (verás un prompt que comienza con un hash `#`). Iniciar sesión como
+el usuario root suele ser aceptable dentro de un contenedor, porque el
+aislamiento del entorno del contenedor limita lo que el usuario root puede
+hacer.
+
+### Protobuf
+
+Ahora vamos a crear un mensaje Protobuf y compilar el archivo `.proto` para
+generar el archivo Ruby relacionado, que contendrá las clases que se copiarán
+en cada una de nuestras aplicaciones Ruby on Rails. Este archivo definirá el
+mensaje Protobuf, las solicitudes y las definiciones de llamadas de
+procedimientos remotos.
+
+Crea un par de directorios para nuestros archivos de entrada y salida. El
+comando `mkdir -p` a continuación creará directorios con la siguiente
+estructura:
+
+* protobuf
+ * definitions
+ * lib
+
+_**Listado 13-5**_ Creando los directorios necesarios
+
+```console
+$ mkdir -p protobuf/{definitions,lib}
+```
+
+Nuestro archivo de definición Protobuf:
+
+_**Listado 13-6**_ Archivo Protobuf del mensaje Employee
+
+```protobuf
+# rails-microservices-sample-code/protobuf/definitions/employee_message.proto
+
+syntax = "proto3";
+
+message EmployeeMessage {
+ string guid = 1;
+ string first_name = 2;
+ string last_name = 3;
+}
+
+message EmployeeMessageRequest {
+ string guid = 1;
+ string first_name = 2;
+ string last_name = 3;
+}
+
+message EmployeeMessageList {
+ repeated EmployeeMessage records = 1;
+}
+
+service EmployeeMessageService {
+ rpc Search (EmployeeMessageRequest) returns (EmployeeMessageList);
+ rpc Create (EmployeeMessage) returns (EmployeeMessage);
+ rpc Update (EmployeeMessage) returns (EmployeeMessage);
+ rpc Destroy (EmployeeMessage) returns (EmployeeMessage);
+}
+```
+
+Para compilar los archivos `.proto`, usaremos una tarea Rake proporcionada por
+la gema `protobuf`. Para acceder a las tareas Rake de la gema `protobuf`,
+necesitaremos crear un `Rakefile`. Hagámoslo ahora.
+
+_**Listing 13-7**_ Rakefile
+
+```ruby
+# rails-microservices-sample-code/protobuf/Rakefile
+
+require 'protobuf/tasks'
+```
+
+Ahora podemos ejecutar la tarea Rake `compile` para generar el archivo.
+
+_**Listado 13-8**_ Iniciando el contenedor builder y compilando la definición
+protobuf
+
+```console
+$ docker-compose -f docker-compose.builder.yml run builder bash
+# cd protobuf
+# rake protobuf:compile
+```
+
+Esto generará un archivo llamado `employee_message.pb.rb` en el directorio
+`protobuf/lib`. Copiaremos este archivo en el directorio `app/lib` de las
+aplicaciones Rails que crearemos a continuación.
+
+### Crear una Aplicación Rails sin una Base de Datos
+
+Llamaremos a esta primera aplicación `active-remote`. Tendrá un modelo, pero
+las clases del modelo heredarán de `ActiveRemote::Base` en lugar del
+`ApplicationRecord` predeterminado (que hereda de `ActiveRecord::Base`). En
+otras palabras, estos modelos interactuarán con los modelos de `active-remote`
+enviando mensajes a través del servidor NATS.
+
+Generemos la aplicación `active-remote`. No necesitaremos la capa de
+persistencia de Active Record, por lo que utilizaremos la bandera
+`--skip-active-record`. Necesitaremos las gemas `active_remote` y
+`protobuf-nats`, pero no la gema `protobuf-activerecord` que incluimos en la
+aplicación `active-record`. Usaremos el scaffolding de Rails para generar un
+modelo, un controlador y vistas para ver y gestionar nuestro protobuf de
+Employee que se compartirá entre nuestras aplicaciones.
+
+_**Listado 13-9**_ Generando las aplicaciones Rails y los archivos necesarios
+
+```console
+$ mkdir chapter-13 # create a directory for this chapter
+$ docker-compose -f docker-compose.builder.yml run builder bash
+# cd chapter-13
+# rails new active-remote --skip-active-record
+# cd active-remote
+# echo "gem 'active_remote'" >> Gemfile
+# echo "gem 'protobuf-nats'" >> Gemfile
+# bundle
+# rails generate scaffold Employee guid:string first_name:string last_name:string
+# exit
+```
+
+We'll need to make a couple of changes to the `active-remote` app. First, let's copy the Protobuf file.
+
+_**Listing 13-10**_ Setting up the app/lib directory and copying the proto class
+
+```console
+$ mkdir chapter-13/active-remote/app/lib
+$ cp protobuf/lib/employee_message.pb.rb chapter-13/active-remote/app/lib/
+```
+
+Ahora editemos el archivo `config/environments/development.rb` para habilitar la carga ansiosa.
+
+_**Listado 13-11**_ Configuración de desarrollo
+
+```ruby
+# rails-microservices-sample-code/chapter-13/active-remote/config/environments/development.rb
+
+...
+config.eager_load = true
+...
+```
+
+Agreguemos el archivo `protobuf_nats.yml`.
+
+_**Listado 13-12**_ Configuración de Protbuf Nats
+
+```yml
+# rails-microservices-sample-code/chapter-13/active-remote/config/protobuf_nats.yml
+
+default: &default
+ servers:
+ - "nats://nats:4222"
+
+development:
+ <<: *default
+```
+
+Ahora agreguemos un modelo que herede de Active Remote.
+
+_**Listado 13-13**_ Modelo Employee de Active Remote
+
+```ruby
+# rails-microservices-sample-code/chapter-13/active-remote/app/models/employee.rb
+
+class Employee < ActiveRemote::Base
+ service_class ::EmployeeMessageService
+
+ attribute :guid
+ attribute :first_name
+ attribute :last_name
+end
+```
+
+Lo último que necesitamos hacer es cambiar un par de llamadas a métodos en el
+archivo `employees_controller.rb` para modificar la forma en que nuestros
+mensajes Protobuf son recuperados e instanciados. Debemos usar el método
+`search` en lugar de los métodos predeterminados `all` y `find` de Active
+Record. Además, como estamos usando uuids (guides) como clave única entre
+servicios, generaremos un nuevo uuid cada vez que se llame a la acción `new`.
+
+_**Listado 13-14**_ Controlador de Employee
+
+```ruby
+# rails-microservices-sample-code/chapter-13/active-remote/controllers/employees_controller.rb
+
+def index
+ @employees = Employee.search({})
+end
+
+...
+
+def new
+ @employee = Employee.new(guid: SecureRandom.uuid)
+end
+
+...
+
+def set_employee
+ @employee = Employee.search(guid: params[:id]).first
+end
+```
+
+### Crear una Aplicación Rails con una Base de Datos
+
+La segunda aplicación Rails que crearemos tendrá su propia base de datos y
+escuchará mensajes a través de NATS mediante Active Remote. También utilizará
+la gema Active Publisher para publicar mensajes en RabbitMQ. Agregaremos las
+gemas requeridas al archivo `Gemfile`. Luego, ejecutaremos el comando `bundle`
+para recuperar las gemas desde https://rubygems.org. Después de recuperar las
+gemas, crearemos el scaffolding para una entidad Employee. Esta aplicación
+almacenará los datos en una base de datos SQLite para que podamos configurar
+los eventos de creación y actualización.
+
+_**Listado 13-15**_ Generando la aplicación Rails y los archivos necesarios
+
+```console
+$ docker-compose -f docker-compose.builder.yml run builder bash
+# cd chapter-13
+# rails new active-record-publisher
+# cd active-record-publisher
+# echo "gem 'active_remote'" >> Gemfile
+# echo "gem 'active_publisher'" >> Gemfile
+# echo "gem 'protobuf-activerecord'" >> Gemfile
+# echo "gem 'protobuf-nats'" >> Gemfile
+# bundle
+# rails generate scaffold Employee guid:string first_name:string last_name:string
+# rails db:migrate
+# exit
+```
+
+Asegúrate de inspeccionar la salida de cada uno de los comandos anteriores,
+buscando errores. Si encuentras errores, por favor verifica cada comando para
+detectar errores tipográficos o caracteres adicionales.
+
+Vamos a personalizar la aplicación para servir nuestra entidad Employee a
+través de Protobuf. Necesitaremos un directorio `app/lib`, y luego copiaremos
+el archivo `employee_message.pb.rb` generado a este directorio.
+
+_**Listado 13-16**_ Generando los directorios de la aplicación Rails y copiando
+la clase de mensaje
+
+```console
+$ mkdir chapter-13/active-record-publisher/app/lib
+$ cp protobuf/lib/employee_message.pb.rb chapter-13/active-record-publisher/app/lib/
+```
+
+A continuación, agregaremos un archivo de configuración `active_publisher` al
+directorio `config`. Este archivo definirá cómo debe conectarse nuestra
+aplicación al servidor RabbitMQ. El host `rabbit` se definirá en el archivo
+`docker-compose` que definiremos en unos minutos.
+
+_**Listado 13-17**_ Configuración de Active Publisher
+
+```yml
+# rails-microservices-sample-code/chapter-13/active-record-publisher/config/active_publisher.yml
+
+default: &default
+ host: rabbit
+ username: guest
+ password: guest
+
+development:
+ <<: *default
+```
+
+Agreguemos el archivo `protobuf_nats.yml`.
+
+_**Listado 13-18**_ Configuración de Protobuf Nats
+
+```yml
+# rails-microservices-sample-code/chapter-13/active-record-publisher/config/protobuf_nats.yml
+
+default: &default
+ servers:
+ - "nats://nats:4222"
+
+development:
+ <<: *default
+```
+
+Ahora vamos a crear un inicializador para Active Publisher. Esto cargará la
+gema, establecerá el adaptador y cargará el archivo de configuración. Vamos a
+crear este archivo en el directorio `config/initializers`.
+
+_**Listado 13-19**_ Inicializador de Active Publisher
+
+```ruby
+# rails-microservices-sample-code/chapter-13/active-record-publisher/config/initializers/active_publisher.rb
+
+require 'active_publisher'
+
+::ActivePublisher::Configuration.configure_from_yaml_and_cli
+```
+
+A continuación, modifiquemos el modelo de empleado para que podamos enviar el
+objeto Protobuf del empleado a RabbitMQ. Utilizaremos callbacks de Active
+Record para publicar mensajes en colas separadas de `created` y `updated`
+después de que se haya creado o modificado un registro de empleado. Abre el
+archivo `app/models/employee.rb` y agrega el siguiente código.
+
+_**Listado 13-20**_ Modelo de Employee
+
+```ruby
+# rails-microservices-sample-code/chapter-13/active-record-publisher/app/models/employee.rb
+
+require 'protobuf'
+
+class Employee < ApplicationRecord
+ protobuf_message :employee_message
+
+ after_create :publish_created
+ after_update :publish_updated
+
+ scope :by_guid, ->(*values) { where(guid: values) }
+ scope :by_first_name, ->(*values) { where(first_name: values) }
+ scope :by_last_name, ->(*values) { where(last_name: values) }
+
+ field_scope :guid
+ field_scope :first_name
+ field_scope :last_name
+
+ def publish_created
+ Rails.logger.info(
+ "Publishing employee object #{inspect} on the employee.created queue."
+ )
+ ::ActivePublisher.publish('employee.created', to_proto.encode, 'events', {})
+ end
+
+ def publish_updated
+ Rails.logger.info(
+ "Publishing employee object #{inspect} on the employee.updated queue."
+ )
+ ::ActivePublisher.publish('employee.updated', to_proto.encode, 'events', {})
+ end
+end
+```
+
+### Crear un Suscriptor de Mensajes
+
+Vamos a crear la aplicación `action-subscriber`. Esta aplicación se suscribirá
+a las colas de mensajes de empleado creado y actualizado y simplemente
+registrará que ha recibido un mensaje en la cola.
+
+_**Listado 13-21**_ Iniciando nuestro contenedor builder
+
+```console
+$ docker-compose -f docker-compose.builder.yml run builder bash
+# cd chapter-13
+# rails new action-subscriber --skip-active-record
+# cd action-subscriber
+# echo "gem 'action_subscriber'" >> Gemfile
+# echo "gem 'protobuf'" >> Gemfile
+# bundle
+# exit
+```
+
+Ahora configuremos Action Subscriber para escuchar eventos. Necesitaremos
+agregar una clase `EmployeeSubscriber` y agregar rutas mediante el método
+`ActionSubscriber.draw_routes`.
+
+Queremos colocar nuestras clases de suscriptor en su propio directorio
+`subscribers`. También necesitaremos el directorio `lib` donde copiaremos
+nuestra clase Protobuf de Employee. Vamos a crear estos directorios y copiar el
+archivo `employee_message.pb.rb` generado a este directorio.
+
+_**Listado 13-22**_ Configuración del directorio app/lib
+
+```console
+$ mkdir chapter-13/action-subscriber/app/{lib,subscribers}
+$ cp protobuf/lib/employee_message.pb.rb chapter-13/action-subscriber/app/lib/
+```
+
+Necesitaremos agregar un directorio de servicios y una clase para escuchar
+mensajes de Active Remote publicados a través de NATS.
+
+_**Listado 13-23**_ Creando el directorio app/services
+
+```console
+$ mkdir chapter-13/active-record-publisher/app/services
+```
+
+_**Listado 13-24**_ Clase de servicio de mensaje de Employee
+
+```ruby
+# rails-microservices-sample-code/chapter-13/active-record-publisher/app/services/employee_message_service.rb
+
+class EmployeeMessageService
+ def search
+ records = ::Employee.search_scope(request).map(&:to_proto)
+
+ respond_with records:
+ end
+
+ def create
+ record = ::Employee.create(request)
+
+ respond_with record
+ end
+
+ def update
+ record = ::Employee.where(guid: request.guid).first
+ record.assign_attributes(request)
+ record.save!
+
+ respond_with record
+ end
+
+ def destroy
+ record = ::Employee.where(guid: request.guid).first
+
+ record.delete
+ respond_with record.to_proto
+ end
+end
+```
+
+Ahora agreguemos la clase de suscriptor. Para los fines de nuestro entorno de
+pruebas, lo mantendremos simple: simplemente registraremos que hemos recibido
+el mensaje.
+
+_**Listado 13-25**_ Clase de suscriptor de Employee
+
+```ruby
+# rails-microservices-sample-code/chapter-13/action-subscriber/app/subscribers/employee_subscriber.rb
+
+class EmployeeSubscriber < ::ActionSubscriber::Base
+ def created
+ Rails.logger.info(
+ "Received created message: #{EmployeeMessage.decode(payload).inspect}"
+ )
+ end
+
+ def updated
+ Rails.logger.info(
+ "Received updated message: #{EmployeeMessage.decode(payload).inspect}"
+ )
+ end
+end
+```
+
+Nuestra aplicación necesita saber a qué colas suscribirse, por lo que usamos el
+método `default_routes_for`, que leerá nuestra clase `EmployeeSubscriber` y
+generará colas para cada uno de nuestros métodos públicos o se suscribirá a
+esas colas si ya existen. El nombre de host `host.docker.internal` es un nombre
+de host especial de Docker; apunta a la dirección IP de la máquina anfitriona.
+
+_**Listado 13-26**_ Inicializador de Action Subscriber
+
+```ruby
+# rails-microservices-sample-code/chapter-13/action-subscriber/config/initializers/action_subscriber.rb
+
+ActionSubscriber.draw_routes do
+ default_routes_for EmployeeSubscriber
+end
+
+ActionSubscriber.configure do |config|
+ config.hosts = ['host.docker.internal']
+ config.port = 5672
+end
+```
+
+Necesitaremos habilitar las configuraciones `cache_classes` y `eager_load`, de
+la misma manera que lo hicimos para el publicador. También necesitaremos
+configurar un registrador (logger) para que podamos ver la salida de los
+registros desde nuestro contenedor Docker.
+
+_**Listado 13-27**_ Configuración de desarrollo
+
+```ruby
+# rails-microservices-sample-code/chapter-13/action-subscriber/config/environments/development.rb
+
+config.cache_classes = true
+...
+config.eager_load = true
+...
+logger = ActiveSupport::Logger.new($stdout)
+logger.formatter = config.log_formatter
+config.logger = ActiveSupport::TaggedLogging.new(logger)
+```
+
+### Crear y Configurar Nuestro Entorno
+
+Por último, pero no menos importante, agreguemos un `Dockerfile` y dos archivos
+Compose: `docker-compose.yml` y `docker-compose-subscriber.yml`. Estos archivos
+se utilizarán para construir una imagen y poner en marcha nuestros contenedores
+Rails, NATS y RabbitMQ. El `Dockerfile` puede que ya exista del sandbox que
+construimos en los capítulos 9 y 12, pero si no es así, tendrá el mismo
+contenido aquí.
+
+_**Listado 13-28**_ Dockerfile del sandbox
+
+```dockerfile
+# rails-microservices-sample-code/Dockerfile
+
+FROM ruby:2.6.5
+
+RUN apt-get update && apt-get install -qq -y --no-install-recommends build-essential nodejs
+
+ENV INSTALL_PATH /usr/src/service
+ENV HOME=$INSTALL_PATH PATH=$INSTALL_PATH/bin:$PATH
+RUN mkdir -p $INSTALL_PATH
+WORKDIR $INSTALL_PATH
+
+RUN gem install rails -v 5.2.4
+
+ADD Gemfile* ./
+RUN set -ex && bundle install --no-deployment
+```
+
+El siguiente archivo Docker Compose incluye una instancia de RabbitMQ y
+nuestras nuevas aplicaciones Rails `active-record-publisher` y `active-remote`.
+Expondremos la aplicación web `active-remote` en el puerto 3002.
+
+Normalmente, agregaríamos el suscriptor al mismo archivo Docker Compose, pero,
+dado que el servicio Action Subscriber intenta conectarse inmediatamente y
+RabbitMQ puede tardar unos segundos en cargar, ejecutaremos el proceso del
+suscriptor desde un archivo Docker Compose separado. También necesitaremos
+exponer el puerto 5672 a la máquina anfitriona para que podamos conectarnos
+desde el otro entorno Compose.
+
+_**Listado 13-29**_ Archivo Docker Compose del sandbox
+
+```yml
+# rails-microservices-sample-code/chapter-13/docker-compose.yml
+# Usage: docker-compose up
+
+version: "3.4"
+
+services:
+ active-record-publisher:
+ environment:
+ - PB_SERVER_TYPE=protobuf/nats/runner
+ build:
+ context: ./active-record-publisher
+ dockerfile: ../../Dockerfile
+ command: bundle exec rpc_server start -p 9399 -o active-record-publisher ./config/environment.rb
+ volumes:
+ - ./active-record-publisher:/usr/src/service
+ depends_on:
+ - nats
+ - rabbit
+ active-remote:
+ environment:
+ - PB_CLIENT_TYPE=protobuf/nats/client
+ build:
+ context: ./active-remote
+ dockerfile: ../../Dockerfile
+ command: bundle exec puma -C config/puma.rb
+ ports:
+ - 3002:3000
+ volumes:
+ - ./active-remote:/usr/src/service
+ depends_on:
+ - nats
+ rabbit:
+ image: rabbitmq:latest
+ ports:
+ - 5672:5672
+ nats:
+ image: nats:latest
+```
+
+Ahora agreguemos el archivo de configuración `action-subscriber`. Ten en cuenta
+que, debido a que el ejecutable de Action Subscriber genera un proceso hijo
+para escuchar eventos de RabbitMQ, perdemos la salida de los registros si
+iniciamos el contenedor usando el comando `up`. Para ver toda la información de
+los registros en el terminal, utilizaremos el comando `run` de Docker Compose
+para iniciar un shell bash y ejecutar nuestro ejecutable `action_subscriber`
+allí.
+
+_**Listado 13-30**_ Archivo Docker Compose para el suscriptor del sandbox
+
+```yml
+# rails-microservices-sample-code/chapter-13/docker-compose-subscriber.yml
+# Usage: docker-compose -f docker-compose-subscriber.yml run action-subscriber bash -c 'bundle exec action_subscriber start'
+
+version: "3.4"
+
+services:
+ action-subscriber:
+ build:
+ context: ./action-subscriber
+ dockerfile: ../../Dockerfile
+ volumes:
+ - ./action-subscriber:/usr/src/service
+```
+
+Ahora que todo está en su lugar, iniciemos nuestras aplicaciones
+`active-remote` y `active-record-publisher`.
+
+_**Listado 13-31**_ Iniciando el sandbox
+
+```console
+$ cd chapter-13
+$ docker-compose up
+```
+
+Una vez que veas líneas como estas, RabbitMQ ha iniciado y la aplicación Rails
+Active Publisher se ha conectado con éxito.
+
+_**Listado 13-32**_ Registro del sandbox
+
+```console
+rabbit_1 | 2020-02-09 22:54:02.253 [info] <0.8.0> Server startup complete; 0 plugins started.
+rabbit_1 | completed with 0 plugins.
+72.30.0.2:5672)
+rabbit_1 | 2020-02-09 22:54:37.395 [info] <0.641.0> connection <0.641.0> (172.30.0.1:53140 -> 172.30.0.2:5672): user 'guest' authenticated and granted access to vhost '/'
+```
+
+Ahora iniciemos el suscriptor en otra ventana de terminal. Podemos usar la
+bandera `-f` para especificar que queremos usar el archivo
+`docker-compose-subscriber.yml`. Ejecute este comando ahora.
+
+_**Listado 13-33**_ Iniciando el sandbox del suscriptor
+
+```console
+$ docker-compose -f docker-compose-subscriber.yml run action-subscriber bash -c 'bundle exec action_subscriber start'
+```
+
+Deberías ver una salida similar a la siguiente.
+
+_**Listado 13-34**_ Registro del sandbox del suscriptor
+
+```console
+I, [2020-03-09T02:54:50.619645 #1] INFO -- : Starting server...
+I, [2020-03-09T02:54:50.667362 #1] INFO -- : Rabbit Hosts: ["host.docker.internal"]
+Rabbit Port: 5672
+Threadpool Size: 8
+Low Priority Subscriber: false
+Decoders:
+ --application/json
+ --text/plain
+
+I, [2020-03-09T02:54:50.667541 #1] INFO -- : Middlewares [
+I, [2020-03-09T02:54:50.667632 #1] INFO -- : [ActionSubscriber::Middleware::ErrorHandler, [], nil]
+I, [2020-03-09T02:54:50.667665 #1] INFO -- : [ActionSubscriber::Middleware::Decoder, [], nil]
+I, [2020-03-09T02:54:50.667681 #1] INFO -- : ]
+I, [2020-03-09T02:54:50.667708 #1] INFO -- : EmployeeSubscriber
+I, [2020-03-09T02:54:50.667766 #1] INFO -- : -- method: created
+I, [2020-03-09T02:54:50.667818 #1] INFO -- : -- threadpool: default (8 threads)
+I, [2020-03-09T02:54:50.667872 #1] INFO -- : -- exchange: events
+I, [2020-03-09T02:54:50.668058 #1] INFO -- : -- queue: actionsubscriber.employee.created
+I, [2020-03-09T02:54:50.668253 #1] INFO -- : -- routing_key: employee.created
+I, [2020-03-09T02:54:50.668350 #1] INFO -- : -- prefetch: 2
+I, [2020-03-09T02:54:50.668401 #1] INFO -- : -- method: updated
+I, [2020-03-09T02:54:50.668603 #1] INFO -- : -- threadpool: default (8 threads)
+I, [2020-03-09T02:54:50.668649 #1] INFO -- : -- exchange: events
+I, [2020-03-09T02:54:50.668682 #1] INFO -- : -- queue: actionsubscriber.employee.updated
+I, [2020-03-09T02:54:50.668711 #1] INFO -- : -- routing_key: employee.updated
+I, [2020-03-09T02:54:50.668737 #1] INFO -- : -- prefetch: 2
+I, [2020-03-09T02:54:50.674268 #1] INFO -- : Action Subscriber connected
+```
+
+Estas líneas de registro indican que el suscriptor se ha conectado al servidor
+con éxito, ha conectado a dos colas y ahora está escuchando eventos.
+
+Vamos a crear un evento. Abre tu navegador y accede a
+[http://localhost:3002/employees](http://localhost:3002/employees). El puerto
+3002 es el puerto que expusimos desde la aplicación Rails `active-remote` en el
+archivo `docker-compose.yml`. Deberías ver una página web simple con el título
+**Employees** y un enlace 'New Employee'. Haz clic en el enlace. Ahora deberías
+poder crear un nuevo registro de empleado en el formulario web. Una vez que lo
+completes y hagas clic en el botón 'Create Employee', sucederán varias cosas.
+Primero, los datos del formulario se enviarán de vuelta a la aplicación Rails
+`active-remote`. El controlador pasará esos datos al modelo Employee de Active
+Remote, que enviará los datos a través de NATS a nuestra aplicación Rails
+`active-record-publisher`. La aplicación `active-record-publisher` creará un
+nuevo registro en la base de datos SQLite. En el modelo
+`active-record-publisher`, el callback `after_create` se ejecutará, codificando
+nuestro mensaje Protobuf y colocándolo en la cola
+`actionsubscriber.employee.created`. RabbitMQ notificará a todos los
+suscriptores que escuchan en una cola específica sobre los nuevos mensajes.
+Nuestra aplicación Rails `action-subscriber` es uno de esos suscriptores. En el
+método `EmployeeSubscriber#created` de nuestro manejador de eventos, escribimos
+código para registrar que recibimos un mensaje. Si inspeccionas la salida de la
+ventana de terminal donde iniciamos la aplicación Rails `action-subscriber`,
+deberías ver información de registro similar a la salida a continuación.
+
+_**Listado 13-35**_ Más registros del sandbox del suscriptor
+
+```console
+I, [2020-03-09T03:03:05.876609 #1] INFO -- : RECEIVED 35f733 from actionsubscriber.employee.created
+I, [2020-03-09T03:03:05.877003 #1] INFO -- : START 35f733 EmployeeSubscriber#created
+Received created message: #
+I, [2020-03-09T03:03:05.877295 #1] INFO -- : FINISHED 35f733
+```
+
+¡Felicidades! Has construido con éxito una plataforma de mensajería que puede
+publicar mensajes a través de Active Remote y aplicaciones que pueden responder
+a eventos. Intenta editar el registro que acabas de crear. Deberías ver una
+salida similar en la aplicación Rails de Action Subscriber mientras recibe y
+procesa el evento y los datos. Para mayor diversión, intenta poner en marcha un
+segundo o tercer servicio de suscriptor que escuche las mismas colas y observa
+cómo todos responden simultáneamente al mismo mensaje publicado.
+
+## Recursos
+
+* https://github.com/kevinwatson/rails-microservices-sample-code
+
+## Conclusión
+
+En este capítulo, hemos construido tres aplicaciones Rails que comparten datos
+utilizando patrones que proporcionan tanto comunicaciones en tiempo real como
+impulsadas por eventos. Cada patrón tiene su lugar. A veces, la comunicación
+necesita ser en tiempo real, como cuando un microservicio necesita acceso a uno
+o más registros. Otras veces, las solicitudes entre servicios no necesitan ser
+en tiempo real, sino impulsadas por eventos, como cuando un servicio necesita
+ser notificado y un proceso prolongado necesita ser iniciado, pero el llamador
+no necesita saber cuándo ha terminado el proceso. Examinar los requisitos de tu
+negocio puede ayudarte a determinar cuándo es necesario cada patrón.
+
+Este capítulo concluye nuestro viaje de construcción de microservicios
+utilizando Ruby on Rails. Hemos cubierto una gran cantidad de terreno. En
+nuestro próximo y último capítulo, resumiremos lo que hemos aprendido hasta
+ahora. También discutiremos algunas formas de continuar nuestro viaje.
+
+[Siguiente >>](150-chapter-14.es.md)
+
+ [alt_text]: images/synchronous-and-event-driven-platform.png "Publicando un mensaje de empleado creado mediante Active Remote y Active Publisher"
diff --git a/140-chapter-13.md b/140-chapter-13.md
index dc25981..a05ac3e 100644
--- a/140-chapter-13.md
+++ b/140-chapter-13.md
@@ -142,7 +142,7 @@ service EmployeeMessageService {
rpc Search (EmployeeMessageRequest) returns (EmployeeMessageList);
rpc Create (EmployeeMessage) returns (EmployeeMessage);
rpc Update (EmployeeMessage) returns (EmployeeMessage);
- rpc Destroy (EmployeeMessage) returns (EmployeeMessage);
+ rpc Destroy (EmployeeMessage) returns (EmployeeMessage);
}
```
@@ -153,7 +153,7 @@ _**Listing 13-7**_ Rakefile
```ruby
# rails-microservices-sample-code/protobuf/Rakefile
-require "protobuf/tasks"
+require 'protobuf/tasks'
```
Now we can run the `compile` Rake task to generate the file.
@@ -248,21 +248,21 @@ _**Listing 13-14**_ Employee controller
```ruby
# rails-microservices-sample-code/chapter-13/active-remote/controllers/employees_controller.rb
- def index
- @employees = Employee.search({})
- end
+def index
+ @employees = Employee.search({})
+end
- ...
+...
- def new
- @employee = Employee.new(guid: SecureRandom.uuid)
- end
+def new
+ @employee = Employee.new(guid: SecureRandom.uuid)
+end
- ...
+...
- def set_employee
- @employee = Employee.search(guid: params[:id]).first
- end
+def set_employee
+ @employee = Employee.search(guid: params[:id]).first
+end
```
### Create a Rails App with a Database
@@ -335,7 +335,7 @@ _**Listing 13-19**_ Active Publisher initializer
```ruby
# rails-microservices-sample-code/chapter-13/active-record-publisher/config/initializers/active_publisher.rb
-require "active_publisher"
+require 'active_publisher'
::ActivePublisher::Configuration.configure_from_yaml_and_cli
```
@@ -355,22 +355,22 @@ class Employee < ApplicationRecord
after_create :publish_created
after_update :publish_updated
- scope :by_guid, lambda { |*values| where(guid: values) }
- scope :by_first_name, lambda { |*values| where(first_name: values) }
- scope :by_last_name, lambda { |*values| where(last_name: values) }
+ scope :by_guid, ->(*values) { where(guid: values) }
+ scope :by_first_name, ->(*values) { where(first_name: values) }
+ scope :by_last_name, ->(*values) { where(last_name: values) }
field_scope :guid
field_scope :first_name
field_scope :last_name
def publish_created
- Rails.logger.info "Publishing employee object #{self.inspect} on the employee.created queue."
- ::ActivePublisher.publish("employee.created", self.to_proto.encode, "events", {})
+ Rails.logger.info "Publishing employee object #{inspect} on the employee.created queue."
+ ::ActivePublisher.publish('employee.created', to_proto.encode, 'events', {})
end
def publish_updated
- Rails.logger.info "Publishing employee object #{self.inspect} on the employee.updated queue."
- ::ActivePublisher.publish("employee.updated", self.to_proto.encode, "events", {})
+ Rails.logger.info "Publishing employee object #{inspect} on the employee.updated queue."
+ ::ActivePublisher.publish('employee.updated', to_proto.encode, 'events', {})
end
end
```
@@ -420,7 +420,7 @@ class EmployeeMessageService
def search
records = ::Employee.search_scope(request).map(&:to_proto)
- respond_with records: records
+ respond_with records:
end
def create
@@ -476,7 +476,7 @@ ActionSubscriber.draw_routes do
end
ActionSubscriber.configure do |config|
- config.hosts = ["host.docker.internal"]
+ config.hosts = ['host.docker.internal']
config.port = 5672
end
```
@@ -492,7 +492,7 @@ config.cache_classes = true
...
config.eager_load = true
...
-logger = ActiveSupport::Logger.new(STDOUT)
+logger = ActiveSupport::Logger.new($stdout)
logger.formatter = config.log_formatter
config.logger = ActiveSupport::TaggedLogging.new(logger)
```
@@ -586,7 +586,7 @@ services:
- ./action-subscriber:/usr/src/service
```
-Now that everything's in place, let's start our active-remote and active-record-publisher apps.
+Now that everything's in place, let's start our active-remote and active-record-publisher apps.
_**Listing 13-31**_ Starting the sandbox
diff --git a/150-chapter-14.es.md b/150-chapter-14.es.md
new file mode 100644
index 0000000..d6ce0ea
--- /dev/null
+++ b/150-chapter-14.es.md
@@ -0,0 +1,22 @@
+### Capítulo 14 - Resumen
+
+Gracias por acompañarme en este viaje de *Construcción de Aplicaciones
+Distribuidas en Rails.* Espero que hayas disfrutado tanto leyendo como yo
+disfruté escribiendo este libro. Mi esperanza es que este libro haya
+desmitificado la construcción de servicios distribuidos y pueda ayudarte a
+diseñar e implementar tu propia plataforma escalable.
+
+En este libro, hemos discutido una arquitectura de microservicios,
+serialización, sistemas de mensajería y diferentes formas de compartir datos.
+Hemos construido microservicios utilizando el marco de Ruby on Rails, lo que
+nos permitió crear nuevas aplicaciones rápidamente con solo unos pocos comandos
+de terminal. Hemos utilizado gemas de código abierto que permiten que nuestras
+aplicaciones se configuren fácilmente y compartan datos utilizando patrones
+tanto en tiempo real como impulsados por eventos. Con esta arquitectura,
+tendrás la flexibilidad para escalar las partes del sistema que crecen a un
+ritmo más rápido o requieren recursos computacionales adicionales.
+
+¡El aprendizaje no termina aquí! Lanza un proyecto de prueba de concepto en el
+trabajo, involúcrate en un grupo de usuarios local y contribuye a esta u otra
+documentación. Como tecnólogos, todos estamos en viajes similares, haciendo
+algo que amamos y al mismo tiempo construyendo nuestros negocios.
diff --git a/999-outline.es.md b/999-outline.es.md
new file mode 100644
index 0000000..df7c090
--- /dev/null
+++ b/999-outline.es.md
@@ -0,0 +1,101 @@
+## Esquema
+
+#### Título: Construcción de Aplicaciones Distribuidas en Rails
+
+1. Portada
+2. Prefacio
+3. Para quién es este libro
+4. Qué contiene este libro
+5. Qué necesitas
+6. Recursos en línea
+7. Agradecimientos
+8. Capítulo 0 - Configuración
+ 1. ¿Por qué Docker?
+ 2. Instalar Docker Desktop
+ 3. Instalar Ruby
+ 4. Resumen
+9. Capítulo 1 - Visión General de Microservicios
+ 1. Monolitos vs microservicios vs función como servicio
+ 2. ¿Qué es un microservicio?
+ 3. ¿Por qué debo diseñar microservicios?
+ 4. Resumen
+10. Capítulo 2 - Comunicaciones entre Servicios
+ 1. Introducción
+ 2. JSON
+ 3. XML
+ 4. Bus de Mensajes
+ 5. Base de Datos Compartida
+ 6. Resumen
+11. Capítulo 3 - Ruby on Rails
+ 1. ¿Por qué Ruby on Rails?
+ 1. El lenguaje Ruby
+ 2. Gestión de paquetes con Bundler
+ 3. Popularidad
+ 4. Generadores de Rails
+ 5. Funcionalidades Incluidas
+ 2. Rails en la empresa
+ 3. Resumen
+12. Capítulo 4 - ActiveRecord y ActiveModel
+ 1. ActiveModel
+ 2. ActiveRecord
+ 3. ActiveResource
+ 4. ¿Cuál es la diferencia?
+ 5. Resumen
+13. Capítulo 5 - ActiveRemote
+ 1. Introducción
+ 2. Filosofía
+ 3. Resumen
+14. Capítulo 6 - Sistemas de Mensajería
+ 1. Visión General
+ 2. Servidor NATS
+ 3. RabbitMQ
+ 4. Configuración
+ 5. Pruebas
+ 6. Monitorización
+ 7. Resumen
+15. Capítulo 7 - Relaciones de Datos
+ 1. Visión General
+ 2. Claves Primarias y Foráneas
+ 3. Natural vs Sustituta
+ 4. Generadas por la Base de Datos vs Generadas por la Aplicación
+ 1. Identificador Único Global vs Identificador Único Universal
+ 5. Cuándo usar cada una
+ 6. Resumen
+16. Capítulo 8 - Protobuf
+ 1. Introducción
+ 2. Filosofía
+ 3. Resumen
+17. Capítulo 9 - Sandbox de Active Remote
+ 1. Configuración
+ 2. Puertos expuestos
+ 3. Pruebas
+ 4. Monitorización
+ 5. Resumen
+18. Capítulo 10 - Mensajería Impulsada por Eventos
+ 1. Introducción
+ 2. Resumen
+19. Capítulo 11 - RabbitMQ
+ 1. Introducción
+ 2. Configuración
+ 3. Pruebas
+ 4. Monitorización
+ 5. Resumen
+20. Capítulo 12 - Sandbox de Mensajería Impulsada por Eventos
+ 1. Introducción
+ 2. Filosofía
+ 3. Configuración
+ 4. Pruebas
+ 5. Resumen
+21. Capítulo 13 - Sandbox de Eventos Impulsados
+ 1. Introducción
+ 2. Configuración
+ 3. Puertos expuestos
+ 4. Pruebas
+ 5. Monitorización
+ 6. Resumen
+22. Capítulo 14 - Combinación de Active Remote y Action Subscriber
+ 1. Introducción
+ 2. Configuración
+ 3. Pruebas
+ 4. Resumen
+23. Capítulo 15 - Resumen
diff --git a/999-scratchpad.es.md b/999-scratchpad.es.md
new file mode 100644
index 0000000..ce6f4cd
--- /dev/null
+++ b/999-scratchpad.es.md
@@ -0,0 +1,18 @@
+# Notas
+
+## Generando clases de protobuf
+
+```console
+$ docker-compose -f docker-compose.builder.yml run builder bash
+# cd protobuf
+# rake protobuf:compile
+```
+
+## Codificando mensajes de protobuf
+
+```console
+# irb
+> require_relative 'lib/person_message.pb'
+> PersonMessage.encode(id: 1, first_name: "George", last_name: "Costanza")
+> PeopleMessageList.encode(records: [PersonMessage.new(id: 1, first_name: "George", last_name: "Costanza"), PersonMessage.new(id: 2, first_name: "Elaine", last_name: "Benes")])
+```
diff --git a/README.es.md b/README.es.md
new file mode 100644
index 0000000..48da004
--- /dev/null
+++ b/README.es.md
@@ -0,0 +1,13 @@
+# Creando Aplicaciones Distribuidas con Rails
+
+## Usando Protocol Buffers, NATS y RabbitMQ
+
+Comencemos por el inicio: [Portada](000-cover.es.md)
+
+Prefacio: [Prefacio](001-preface.es.md)
+
+Tabla de Contenido: [Tabla de Contenido](008-table-of-contents.es.md)
+
+[Código relacionado][]
+
+[Código relacionado]: https://github.com/kevinwatson/rails-microservices-sample-code
diff --git a/README.md b/README.md
index 8ec93a1..f112422 100644
--- a/README.md
+++ b/README.md
@@ -9,3 +9,5 @@ Preface: [Preface](001-preface.md)
Table of Contents: [Table of Contents](008-table-of-contents.md)
Related code: https://github.com/kevinwatson/rails-microservices-sample-code
+
+Spanish version: [here](README.es.md)
diff --git a/TODO.es.md b/TODO.es.md
new file mode 100644
index 0000000..a2099c6
--- /dev/null
+++ b/TODO.es.md
@@ -0,0 +1,24 @@
+# TODO
+
+* Créditos a [MX][]
+
+* Traducción [Luilver Garcés][]
+
+* Proceso de edición [Rubocop][]
+ - rubocop -r "rubocop-md" .
+
+* Usamos [Writebook][] en el estilo y apariencia de este libro.
+
+* Si notas errores tipográficos u otros problemas, siéntete libre de abrir un
+ issue en [GitHub][] o enviar una solicitud de extracción. Si haces esto
+ último, en tu mensaje de confirmación, por favor añade la frase “Asigno los
+ derechos de autor de esta contribución a Kevin Watson,” para que pueda
+ mantener la opción de publicar este libro en otras formas.
+
+---
+
+ [Github]: https://github.com/kevinwatson/rails-microservices-book
+ [Luilver Garcés]: https://luilver.com
+ [MX]: https://mx.com
+ [Rubocop]: https://rubocop.org
+ [Writebook]: https://once.com/writebook