Cómo configurar una arquitectura de microservicios en Ruby: una guía paso a paso
Publicado: 2022-03-11¿Qué son los microservicios?
Los microservicios son una de las últimas tendencias en el diseño de software donde múltiples servicios independientes se comunican entre sí y tienen sus propios procesos y recursos. Este enfoque difiere del diseño típico de una aplicación cliente-servidor. La aplicación cliente-servidor habitual consta de uno o más clientes, un back-end monolítico que incluye todos los datos y la lógica del dominio, y una API que permite a los clientes acceder al back-end y su funcionalidad.
En una arquitectura de microservicios, el backend monolítico descrito se sustituye por un conjunto de servicios distribuidos. Este diseño permite una mejor separación de responsabilidades, un mantenimiento más sencillo, una mayor flexibilidad en la elección de tecnologías para cada servicio y una escalabilidad y tolerancia a fallos más sencilla. Al mismo tiempo, los sistemas distribuidos complejos tienen su conjunto de desafíos. Tienen más posibilidades de tener que lidiar con condiciones de carrera y son más difíciles de depurar, ya que los problemas no se identifican fácilmente en un solo servicio, sino que se distribuyen a través de muchos. Si no se hace un esfuerzo por seguir las mejores prácticas al construir un sistema de este tipo, es posible que se encuentre rodeado de incendios que no sepa cómo apagar. Se debe tener especial cuidado con los contratos de carga útil de los servicios, ya que los cambios en un servicio pueden afectar a todos sus clientes y, en consecuencia, a todo el conjunto de servicios del back-end.
Todas estas consideraciones son importantes, pero supongamos que ya las ha pensado. Ahora lo que desea es encontrar una forma de crear un back-end de microservicios por su cuenta. Así que profundicemos en eso.
Cómo configurar una arquitectura de microservicios
Actualmente, existen muchas formas de configurar sus microservicios y, en esta guía, nos centraremos en la arquitectura de un intermediario.
Una arquitectura de intermediario
Una arquitectura de intermediario es una de las formas en que puede hacer que sus servicios se comuniquen entre sí. En él, todos los servicios rodean un servidor de mensajería, el intermediario, y todos están conectados a él. Los servicios envían mensajes al corredor, quien luego sabe qué otro servicio o servicios necesita para reenviar estos mensajes. De esta forma, los servicios no necesitan guardar información sobre otros servicios. En cambio, confían en el corredor para que se encargue de todos los mensajes, y les permite estar aislados y enfocados solo en su dominio particular. Un corredor también puede almacenar mensajes cuando sus receptores están inactivos, lo que permite que los remitentes y los receptores no se vean obligados a estar activos simultáneamente, lo que permite un aislamiento aún mayor. Por supuesto, esta solución tiene inconvenientes, ya que el intermediario puede convertirse rápidamente en un cuello de botella, ya que toda la comunicación debe pasar por él, y también puede convertirse en un único punto de falla para su backend. Sin embargo, hay algunas maneras de mitigar estos problemas. Una forma es tener varias instancias del intermediario ejecutándose en paralelo, lo que permitiría una mejor tolerancia a fallas del sistema. Otra forma sería utilizando otras arquitecturas. Las arquitecturas alternativas difieren de la arquitectura que implementaremos en esta guía al no usar un intermediario, o al usar una arquitectura de intermediario diferente, o al usar un protocolo de mensajería diferente, como HTTP.
Comunicación entre servicios
En esta guía, usaremos ZeroMQ para manejar la comunicación entre los servicios y el intermediario.
ZeroMQ proporciona una capa de abstracción de protocolo que maneja mensajes asíncronos de varias partes en transportes aleatorios. Las ventajas de usar ZeroMQ para la mensajería entre los servicios y el corredor están fuera del alcance de esta guía, por lo que no las analizaremos aquí, pero si desea saber más sobre ellas, consulte el siguiente artículo de Quora. Si está interesado en descubrir otras formas de hacer que sus servicios se comuniquen entre sí, le sugiero que eche un vistazo al artículo Broker vs. Brokerless para ver qué más se puede lograr.
Creación de la suite de microservicios
Este artículo lo guiará a través de todos los pasos necesarios para crear su conjunto de microservicios. Nuestro sistema consistirá en un corredor y un servicio. También usaremos un pequeño script de cliente para probar las llamadas al conjunto de servicios, pero tenga en cuenta que el código de cliente se puede usar fácilmente en cualquier lugar.
Entonces, comencemos a construir.
Empezando
Primero, asegurémonos de que tiene todo lo que necesita para ejecutar el corredor y el servicio. Primero, comience descargando e instalando Node.js, ZeroMQ y Git en su máquina. Si está utilizando OSX, hay paquetes caseros para cada uno de ellos, y la mayoría de las distribuciones de Linux también tienen un paquete para cada uno de ellos, por lo que no debería tener problemas con esto. Los usuarios de Windows pueden simplemente usar los enlaces de descarga proporcionados anteriormente.
Ejecución del corredor
Después de instalar todas las dependencias requeridas, pongamos en marcha nuestro agente. En esta guía, estamos utilizando una implementación de Node.js del intermediario que forma parte de la Suite orientada a servicios de ZMQ. Puede encontrar su código y documentación en GitHub. Para ejecutar el bróker, primero clone un bootstrap de bróker en su máquina. Este repositorio es un arranque para usar la biblioteca de intermediarios anterior. Tenga en cuenta que este paso no es necesario ya que la biblioteca original es ejecutable, pero la diferencia entre los dos es que en el repositorio de arranque puede cambiar las configuraciones predeterminadas.
Entonces, primero, use el siguiente comando Git para descargar el proyecto a su máquina:
$ git clone [email protected]:dadah/zmq-broker-bootstrap.git
Después de haber hecho eso, vaya al directorio creado:
$ cd zmq-broker-bootstrap
Ahora instale las dependencias del paquete:
$ npm install
El corredor ya está listo. Para ejecutar su corredor, ejecute el siguiente comando:
$ bin/zss-broker run
Puede encontrar archivos de configuración para cada entorno en el directorio config/
. Esta es la configuración de desarrollo por defecto:
{ "broker": { "backend": "tcp://127.0.0.1:7776", "frontend": "tcp://127.0.0.1:7777" }, "log": { "consolePlugin": { "level": "debug" } } }
El parámetro de backend
-end define la dirección ip:port
del back-end y front-end del corredor. La dirección de back-end es donde el corredor recibe solicitudes y responde a los servicios, y la dirección de front-end es donde recibe y envía a los clientes del servicio. También puede establecer el nivel de registro cambiando log.consolePlugin.level
. Los valores posibles son trace
, debug
, info
, warn
y error
, y determinan la cantidad de información de registro que generará el proceso del intermediario.
Ejecutando el Servicio
Una vez que haya creado su corredor, es hora de desarrollar su primer microservicio de Ruby. Comience abriendo una nueva ventana de consola. Luego, cree un directorio donde se almacenarán sus servicios y luego vaya a ese directorio. En esta guía, estamos utilizando el cliente Ruby y el servicio de ZMQ SOA Suite. Hay un servicio de arranque "Hello world" disponible, así que usémoslo para ejecutar nuestro primer microservicio.
Vaya a su directorio de servicios y clone el repositorio de arranque:
$ git clone [email protected]:dadah/zmq-service-suite-ruby-bootstrap.git
Vaya al directorio recién creado:
$ cd zmq-service-suite-ruby-bootstrap
Ahora instale todas las dependencias:
$ bundle install
Para iniciar el servicio, ejecute el siguiente comando:
$ bin/zss-service run
Genial. Ya tienes tu primer servicio en funcionamiento.
Si va a la ventana de la consola donde dejó su corredor en ejecución, puede ver el siguiente resultado:
2015-12-15 16:45:05 | INFO | BROKER - Async Broker is waiting for messages... 2015-12-15 16:45:14 | DEBUG | BACKEND - received from: hello-word#aaa65374-8585-410a-a41d-c8a5b024553b rid: 76f50741-913a-43b9-94b0-36d8f7bd75b1 2015-12-15 16:45:14 | DEBUG | BACKEND - routing from: hello-word#aaa65374-8585-410a-a41d-c8a5b024553b rid: 76f50741-913a-43b9-94b0-36d8f7bd75b1 to SMI.UP request... 2015-12-15 16:45:14 | INFO | SMI - SMI register for sid: HELLO-WORD instance: hello-word#aaa65374-8585-410a-a41d-c8a5b024553b! 2015-12-15 16:45:14 | DEBUG | BACKEND - reply to: hello-word#aaa65374-8585-410a-a41d-c8a5b024553b rid: 76f50741-913a-43b9-94b0-36d8f7bd75b1 with status: 200 2015-12-15 16:45:15 | DEBUG | BACKEND - received from: hello-word#aaa65374-8585-410a-a41d-c8a5b024553b rid: 3b3a0416-73fa-4fd2-9306-dad18bc0502a 2015-12-15 16:45:15 | DEBUG | BACKEND - routing from: hello-word#aaa65374-8585-410a-a41d-c8a5b024553b rid: 3b3a0416-73fa-4fd2-9306-dad18bc0502a to SMI.HEARTBEAT request... 2015-12-15 16:45:15 | DEBUG | BACKEND - reply to: hello-word#aaa65374-8585-410a-a41d-c8a5b024553b rid: 3b3a0416-73fa-4fd2-9306-dad18bc0502a with status: 200 2015-12-15 16:45:16 | DEBUG | BACKEND - received from: hello-word#aaa65374-8585-410a-a41d-c8a5b024553b rid: b3044c24-c823-4394-8204-1e872f30e909 2015-12-15 16:45:16 | DEBUG | BACKEND - routing from: hello-word#aaa65374-8585-410a-a41d-c8a5b024553b rid: b3044c24-c823-4394-8204-1e872f30e909 to SMI.HEARTBEAT request... 2015-12-15 16:45:16 | DEBUG | BACKEND - reply to: hello-word#aaa65374-8585-410a-a41d-c8a5b024553b rid: b3044c24-c823-4394-8204-1e872f30e909 with status: 200
Este registro significa que el intermediario ha reconocido la existencia de un nuevo servicio y está recibiendo mensajes de latido del mismo. Cada segundo, el servicio envía un mensaje de latido al corredor, para que sepa que la instancia del servicio está activa.
Consumir del Servicio
Entonces, ahora que tenemos un servicio en ejecución, ¿cómo lo usamos?
En el repositorio de arranque, hay un cliente ficticio que puede usar para probar su servicio "Hello World". Simplemente abra una nueva ventana o pestaña de la consola y vaya a su directorio de servicios. Una vez que esté allí, ejecute el siguiente comando:
$ bin/zss-client
Debería ver algo como esto:
15-49-15 16:49:54 | INFO | ZSS::CLIENT - Request 90a88081-3485-45b6-91b3-b0609d64592a sent to HELLO-WORD:*#HELLO/WORLD with 1.0s timeout 15-49-15 16:49:54 | INFO | ZSS::CLIENT - Received response to 90a88081-3485-45b6-91b3-b0609d64592a with status 200 "Hello World"
Si va a la ventana de la consola donde se ejecuta su servicio, debería ver esto:
Started hello-word daemon... 15-45-15 16:45:14 | INFO | ZSS::SERVICE - Starting SID: 'HELLO-WORD' ID: 'hello-word#aaa65374-8585-410a-a41d-c8a5b024553b' Env: 'development' Broker: 'tcp://127.0.0.1:7776' 15-49-15 16:49:54 | INFO | ZSS::SERVICE - Handle request for HELLO-WORD:*#HELLO/WORLD 15-49-15 16:49:54 | INFO | ZSS::SERVICE - Reply with status: 200
Bueno. Acaba de lanzar y consumir su microservicio "Hello World". Esto no es, sin embargo, lo que nos propusimos hacer. Queremos construir nuestro(s) servicio(s). Vamos a ello, entonces.
Construyendo su servicio
Primero, detengamos nuestro servicio "Hello World". Vaya a la ventana de la consola del servicio y presione Ctrl+C
para detener el servicio. A continuación, debemos convertir nuestro servicio "Hola mundo" en el servicio "Persona".
Estructura del código
Empecemos echando un vistazo al árbol de código del proyecto. Se parece a esto:
- El directorio
bin
es donde almacena los scripts que inician su servicio. - El directorio de
config
almacena todos los archivos de configuración.- El archivo
boot.rb
es donde puede agregar todas las dependencias de su servicio. Si lo abre, puede notar que ya hay muchas dependencias enumeradas allí. Si necesita agregar más, aquí es donde debe hacerlo. - El archivo
application.yml
almacena todas las configuraciones de su aplicación. Echaremos un vistazo a este archivo más tarde. - En el directorio
config/initializers
puede agregar sus scripts de inicialización. Puede, por ejemplo, agregar configuraciones para conexiones ActiveRecord o Redis aquí. Los scripts que agregue a este directorio se ejecutarán al iniciar el servicio.
- El archivo
- En el directorio
db/migrate
puede almacenar sus migraciones ActiveRecord o Sequel si tiene alguna. En caso de que no lo haga, puede eliminar este directorio por completo. - El directorio
lib
es donde reside el código de la aplicación principal.- El archivo
settings.rb
simplemente carga el archivoapplication.yml
y lo pone a disposición en todo el alcance del servicio, para que pueda acceder a sus configuraciones en cualquier lugar. Por ejemplo,Settings.broker.backend
devuelve la dirección de backend del intermediario que definió en el archivo YML anterior. - El archivo
service_register.rb
es donde registra sus servicios y rutas de servicio. Lo explicaremos más adelante. - El archivo
hello_world_service.rb
define los puntos finales del servicio "Hello World". - El directorio
lib/daos
es donde almacena sus objetos ActiveModel si está utilizando ActiveRecord, o cualquier otro objeto de acceso a datos que eventualmente pueda crear, como sus modelos Sequel. - El directorio
lib/dtos
almacena sus objetos de transferencia de datos. Estos objetos son los que finalmente se devuelven a los clientes del servicio. - El directorio
lib/repositories
almacena sus repositorios. Los repositorios son objetos que permiten que los servicios accedan a los datos y son los únicos objetos que pueden manejar DAO. Entonces, si un servicio quiere un grupo de instancias de "Hello World", se las pedirá al repositorio. El repositorio, a su vez, utiliza los DAO apropiados para obtener los datos relevantes de la base de datos. Luego, los datos se asignan a una colección de DTO "HelloWorld" o "HelloWorld" DTO adecuada que se devuelve al servicio. - El directorio
lib/repositories/mappers
es donde almacena sus mapeadores. Los mapeadores son objetos que convierten DAO en DTO y viceversa.
- El archivo
El archivo application.yml
del directorio de config
se ve así:

defaults: &defaults broker: backend: tcp://127.0.0.1:7776 frontend: tcp://127.0.0.1:7777 logging: console: level: info development: <<: *defaults test: <<: *defaults production: <<: *defaults
Esta configuración simplemente establece la dirección de back-end y front-end del corredor y el nivel de registro.
Si todo esto suena confuso hasta ahora, no se preocupe, ya que se volverá más claro a medida que avancemos.
Servicio “Persona”
Entonces, sigamos con nuestro servicio “Persona”. Comencemos configurando la conexión de la base de datos. Abra el archivo config/initializers/active_record.rb
y elimine el comentario de la única línea allí. Luego, agregue la siguiente entrada a su configuración de desarrollo en application.yml
para que se vea así:
defaults: &defaults broker: backend: tcp://127.0.0.1:7776 frontend: tcp://127.0.0.1:7777 logging: console: level: info database: adapter: postgresql database: zss-tutorial-development
Ahora que agregó la configuración de su base de datos, debe crear la base de datos. En este momento, no hay forma de hacer esto automáticamente a menos que esté utilizando una base de datos PostgreSQL predeterminada, en cuyo caso simplemente puede ejecutar:
$ rake db:create
Si prefiere otra base de datos, debe agregar la gema adecuada al gemfile y luego instalar el proyecto en paquete.
Lo siguiente es la migración. Para eso, simplemente cree el archivo db/migrate
migrate llamado 000_creates_persons.rb
:
$ touch db/migrate/000_creates_persons_table.rb
Abra el archivo y cree la migración como lo haría con una migración normal de Rails:
class CreatesPersons < ActiveRecord::Migration def change create_table :persons do |t| t.name t.timestamps end end end
A continuación, ejecútalo:
$ rake db:migrate == 0 CreatesPersons: migrating ================================================ -- create_table(:persons) DEPRECATION WARNING: `#timestamp` was called without specifying an option for `null`. In Rails 5, this behavior will change to `null: false`. You should manually specify `null: true` to prevent the behavior of your existing migrations from changing. (called from block in change at /Users/francisco/Code/microservices-tutorial/db/migrate/000_creates_persons.rb:6) -> 0.0012s == 0 CreatesPersons: migrated (0.0013s) =======================================
Ahora que tenemos nuestra tabla creada, creemos un modelo para ella. Cree el archivo lib/daos/person.rb
:
$ touch lib/daos/person.rb
Edítalo así:
module DAO class Person < ActiveRecord::Base end end
Ahí está tu modelo. Ahora necesita crear un modelo DTO para una "Persona" para poder devolverlo al cliente. Cree el archivo lib/dtos/person.rb
:
$ touch lib/dtos/person.rb
Edítalo así:
module DTO class Person < Base attr_reader :id, :name end end
A continuación, debe crear un Mapeador para convertir el DAO "Persona" en un DTO "Persona". Cree el archivo lib/repositories/mappers/person.rb
y edítelo así:
module Mapper class Person < Mapper::Base def self.to_dao dto_instance DAO::Person.new id: dto_instance.id, name: dto_instance.name end def self.to_dto dao_instance DTO::Person.new id: dao_instance.id, name: dao_instance.name end end end
Aquí, Mapper::Base
requiere que implementes self.to_dao
y self.to_dto
. Si no desea hacerlo, puede implementar self.map
en su lugar y anular el Mapper::Base.map
que llama a to_dao
o to_dto
, dependiendo de si el atributo que recibe es un DAO o un DTO.
Ahora tiene un DAO para acceder a su base de datos, un DTO para enviarlo al cliente y un Mapeador para convertir uno en otro. Ahora puede usar estas tres clases dentro de un repositorio para crear la lógica que le permite obtener personas de la base de datos y devolver una colección correspondiente de DTO.
Vamos a crear el repositorio entonces. Cree el archivo lib/repositories/person.rb
:
$ touch lib/dtos/person.rb
Edítalo así:
module Repository class Person < Repository::Base def get DAO::Person.all.map do |person| Mapper::Person.map(person) end end end end
Este repositorio solo tiene el método de instancia get
, que simplemente obtiene a todas las personas de la base de datos y las asigna a una colección de DTO de personas, bastante simple. Juntemos todo esto ahora. Todo lo que queda ahora es crear el servicio y el punto final que llama a este repositorio. Para hacer eso, creemos el archivo lib/person_service.rb
:
$ touch lib/person_service.rb
Edítalo así:
class PersonService < BaseService attr_reader :person_repo def initialize @person_repo = Repository::Person.new end def get payload, headers persons = person_repo.get() if persons.empty? raise ZSS::Error.new(404, "No people here") else persons.map &:serialize end end end
El servicio “Persona” inicializa el repositorio en su inicializador. Todos los métodos de instancia pública del servicio "Persona" tienen carga útil y encabezados que puede omitir si no los necesita. Ambos son instancias de Hashie::Mash
y almacenan las variables enviadas al punto final, ya sea como atributos o encabezados, y sus respuestas imitan las respuestas HTTP, ya que cada respuesta tiene un código de estado que los clientes pueden usar para averiguar el resultado de las solicitudes enviadas al servicio, junto con la carga de respuesta del servicio. Los códigos de respuesta son los mismos que esperaría de un servidor HTTP. Por ejemplo, una solicitud exitosa devolverá un código de estado 200, junto con la carga de respuesta. Si ocurre algún error de servicio, el código de estado será 500, y si hay algún problema con los parámetros enviados al servidor, el código de estado será 400. El servicio puede responder con la mayoría de los códigos de estado HTTP junto con su carga útil. Entonces, si, por ejemplo, desea que su servicio le diga a sus clientes cuándo no pueden acceder a un punto final determinado, puede hacerlo respondiendo con un código 403. Puede ver otro ejemplo de códigos de respuesta si mira hacia atrás en nuestro código de servicio anterior. En el punto final de get
, devolvemos el código de estado 404 junto con el mensaje opcional "No hay personas aquí" cuando no se encuentran personas, al igual que un servidor HTTP devolvería un 404 si no hay recursos disponibles. Si el repositorio devuelve personas, entonces el servicio serializa los DTO y los devuelve al cliente. Cada DTO tiene un serializador predeterminado que devuelve un objeto JSON con las claves y los valores correspondientes definidos como attr_reader
o attr_accessible
en la definición de DTO. Por supuesto, puede anular el serializador definiendo su método de serialización en sus clases DTO.
Ahora que tenemos un servicio definido, necesitamos registrarlo. Este es el paso final. Abra el archivo lib/service_register.rb
y sustituya todas las ocurrencias de "HelloWorld" con "Person", para que el archivo finalmente se vea así:
module ZSS class ServiceRegister def self.get_service config = Hashie::Mash.new( backend: Settings.broker.backend ) service = ZSS::Service.new(:person, config) personInstance = PersonService.new service.add_route(personInstance, :get) return service end end end
Como probablemente notó, hay un pequeño cambio en la llamada add_route
. Eliminamos la cadena "HOLA/MUNDO". Esto se debe a que la cadena solo se necesita si el verbo de servicio no coincide con el método que lo implementa. En nuestro caso, al llamar al servicio de persona con el verbo GET, el método a llamar es get
, por lo que podemos omitir la cadena.
La clase ServiceRegister
es donde debe definir el método self.get_service
. Este método inicializa el servicio y lo conecta al backend del corredor. Luego hace coincidir las rutas en ese servicio con los métodos en una o más definiciones de servicio. Por ejemplo, en el siguiente caso, crea el servicio y lo vincula al intermediario:
config = Hashie::Mash.new( backend: Settings.broker.backend ) service = ZSS::Service.new(:person, config)
Luego crea una instancia de un controlador de servicio:
personInstance = PersonService.new
A continuación, el controlador de servicios está vinculado al servicio:
service.add_route(personInstance, :get)
Finalmente, debe devolver la instancia de servicio.
return service
Ahora, solo queda un último paso antes de que podamos lanzar nuestro servicio "Persona"; necesitamos crear un script ejecutable para ello. Ya tenemos uno para el "HelloService". Por lo tanto, abra el archivo bin/zss-service
, sustituya "hello-word" por "person" y guarde el archivo. Vuelve a la consola y ejecuta:
$ bin/zss-service run Starting person: PID: ./log LOGS: ./log Started person daemon... 15-29-15 19:29:54 | INFO | ZSS::SERVICE - Starting SID: 'PERSON' ID: 'person#d3ca7e1f-e229-4502-ac2d-0c01d8c285f8' Env: 'development' Broker: 'tcp://127.0.0.1:7776'
Eso es todo. Acaba de iniciar su servicio "Persona" por primera vez. Ahora vamos a probarlo. Abra el archivo bin/zss-client
, cambie la variable sid
a "persona" y cambie la llamada del cliente de hello_world()
a get()
. Una vez hecho esto, ejecute el cliente en una nueva ventana:
$ bin/zss-client /Users/francisco/.rvm/gems/ruby-2.1.2/gems/zss-0.3.4/lib/zss/client.rb:41:in `new': No people here (ZSS::Error) from /Users/francisco/.rvm/gems/ruby-2.1.2/gems/zss-0.3.4/lib/zss/client.rb:41:in `call' from /Users/francisco/.rvm/gems/ruby-2.1.2/gems/zss-0.3.4/lib/zss/client.rb:55:in `method_missing' from bin/zss-client:12:in `<main>'
Como puede ver, ha detectado un ZSS::Error
. Esto se debe a que generamos un error cuando el servicio no encuentra personas y todavía no tenemos personas en la base de datos de nuestro servicio.
Manejemos este error entonces. Abra zss-client
y edítelo así:
begin client = ZSS::Client.new(sid, config) p client.get() rescue ZSS::Client => e if e.code == 404 p e.message else raise e end end
Ahora estamos imprimiendo el mensaje de error cuando el código de error es 404, mientras lanzamos el error si es uno diferente. Veámoslo en acción ejecutando nuestro cliente nuevamente:
$ bin/zss-client "No people here"
Excelente. Agreguemos ahora algunas personas a nuestra mesa y veamos si el servicio las devuelve a nuestro cliente. Para hacer esto, simplemente abra una consola de servicio:
$ rake service:console
Agrega algunas personas:
$ rake service:console [1] pry(main)> DAO::Person.create name: 'John' => #<DAO::Person:0x007fe51bbe9d00 id: 1, name: "John", created_at: 2015-12-16 13:22:37 UTC, updated_at: 2015-12-16 13:22:37 UTC> [2] pry(main)> DAO::Person.create name: 'Mary' => #<DAO::Person:0x007fe51c1dafe8 id: 2, name: "Mary", created_at: 2015-12-16 13:22:42 UTC, updated_at: 2015-12-16 13:22:42 UTC> [3] pry(main)> DAO::Person.create name: 'Francis' => #<DAO::Person:0x007fe51bc11698 id: 3, name: "Francis", created_at: 2015-12-16 13:22:53 UTC, updated_at: 2015-12-16 13:22:53 UTC> [4] pry(main)> exit
Ahora, ejecuta tu cliente de nuevo.
$ bin/zss-client [{"id"=>1, "name"=>"John"}, {"id"=>2, "name"=>"Mary"}, {"id"=>3, "name"=>"Francis"}]
Ahí tienes.
Consideraciones finales
Al revisar el código provisto en esta guía, podría pensar que hay muchos pasos que eran innecesarios, como la creación de repositorios o DTO, y estaría en lo cierto. Todo lo que necesitaría para tener un servicio de "Persona" en funcionamiento sería su clase de servicio y su DAO, a la que podría llamar directamente desde la clase de servicio. No obstante, es una buena práctica seguir el patrón descrito en este artículo, ya que le permite mantener la lógica del servicio separada de la manipulación del almacenamiento de datos. Los servicios solo deben centrarse en su lógica, y los repositorios deben manejar todas las interacciones con su almacenamiento de datos. Los DTO determinan las cargas útiles y la serialización de sus servicios, y los DAO solo se ocupan de obtener datos del almacenamiento. Las convenciones y técnicas descritas en esta guía se conocen como el patrón de repositorio, que puede consultar en la imagen a continuación.
Me gustaría terminar pidiendo a cualquiera que lo encuentre útil que contribuya al conjunto de servicios SOA, expandiéndolo y mejorándolo de alguna manera. Todas sus bifurcaciones y solicitudes de extracción son bienvenidas.
Espero que esto te ayude a iniciarte en los microservicios. Si desea consultar el código de servicio, hay una versión completa disponible en GitHub.