Como configurar uma arquitetura de microsserviços em Ruby: um guia passo a passo

Publicados: 2022-03-11

O que são microsserviços?

Os microsserviços são uma das últimas tendências em design de software onde vários serviços independentes se comunicam entre si e possuem seus próprios processos e recursos. Essa abordagem difere de um design de aplicativo cliente-servidor típico. A aplicação cliente-servidor usual consiste em um ou mais clientes, um back-end monolítico que inclui todos os dados e lógica do domínio e uma API que permite aos clientes acessar o back-end e suas funcionalidades.

Os microsserviços estão substituindo os servidores de back-end monolíticos clássicos
Tweet

Em uma arquitetura de microsserviços, o back-end monolítico descrito é substituído por um conjunto de serviços distribuídos. Esse design permite uma melhor separação de responsabilidades, manutenção mais fácil, maior flexibilidade na escolha de tecnologias para cada serviço e escalabilidade e tolerância a falhas mais fáceis. Ao mesmo tempo, sistemas distribuídos complexos têm seu conjunto de desafios. Eles têm uma chance maior de ter que lidar com condições de corrida e são mais difíceis de depurar, pois os problemas não são facilmente identificados em um único serviço, mas são distribuídos por muitos. Se não for feito um esforço para seguir as melhores práticas durante a construção de tal sistema, você pode se ver cercado por incêndios que não sabe como apagar. Cuidados especiais devem ser tomados com os contratos de carga útil dos serviços, pois alterações em um serviço podem afetar todos os seus clientes e, consequentemente, todo o conjunto de serviços do back-end.

Todas essas considerações são importantes, mas vamos supor que você já tenha pensado nelas. Agora, o que você quer é encontrar uma maneira de construir um back-end de microsserviços por conta própria. Então vamos mergulhar direto nisso.

Como configurar uma arquitetura de microsserviços

Atualmente, existem muitas maneiras de configurar seus microsserviços e, neste guia, focaremos em uma arquitetura de corretor.

Arquitetura de um corretor

A arquitetura do agente, com um agente (B) no meio e quatro microsserviços ao seu redor, chame-os de N, S, E, W. O caminho de solicitação/resposta começa em uma entrada fora da arquitetura e segue o caminho N, B, E, B, S, B, W, B, E, B, N, antes de finalmente sair como saída.

Uma arquitetura de corretor é uma das maneiras de fazer com que seus serviços se comuniquem entre si
Tweet

Uma arquitetura de corretor é uma das maneiras pelas quais você pode fazer com que seus serviços se comuniquem entre si. Nele, todos os serviços envolvem um servidor de mensagens, o broker, e todos estão conectados a ele. Os serviços enviam mensagens para o corretor, que então sabe qual outro serviço ou serviços ele precisa para encaminhar essas mensagens. Dessa forma, os serviços não precisam manter informações sobre outros serviços. Em vez disso, eles contam com o corretor para cuidar de todas as mensagens e isso permite que eles sejam isolados e focados apenas em seu domínio específico. Um broker também pode armazenar mensagens quando seus receptores estão inativos, permitindo que remetentes e receptores não sejam forçados a estarem ativos simultaneamente, permitindo assim um isolamento ainda maior. É claro que há desvantagens nessa solução, pois o corretor pode rapidamente se tornar um gargalo, pois toda a comunicação deve passar por eles, e também pode se tornar um único ponto de falha para o seu back-end. No entanto, existem algumas maneiras de mitigar esses problemas. Uma maneira é ter várias instâncias do broker executando em paralelo, o que permitiria uma melhor tolerância a falhas do sistema. Outra maneira seria usar outras arquiteturas. Arquiteturas alternativas diferem da arquitetura que implementaremos neste guia por não usar um broker ou usar uma arquitetura de broker diferente ou usar um protocolo de mensagens diferente, como HTTP.

Comunicação entre serviços

Neste guia, usaremos o ZeroMQ para lidar com a comunicação entre os serviços e o broker.

A pilha ZeroMQ. No topo há um bloco com reticências e depois o sinal ZeroMQ. Um bloco inferior tem, de cima para baixo: transporte, rede, enlace de dados e físico.

O ZeroMQ fornece uma camada de abstração de protocolo que lida com mensagens assíncronas de várias partes em transportes aleatórios. As vantagens de usar o ZeroMQ para mensagens entre serviços e corretor estão fora do escopo deste guia, portanto, não as abordaremos aqui, mas se você quiser saber mais sobre elas, confira o seguinte artigo do Quora. Se você estiver interessado em descobrir outras maneiras de fazer com que seus serviços conversem entre si, sugiro que dê uma olhada no artigo Broker vs. Brokerless para ver o que mais pode ser alcançado.

Construindo o pacote de microsserviços

Este artigo o guiará por todas as etapas necessárias para criar seu conjunto de microsserviços. Nosso sistema será composto por um corretor e um serviço. Também usaremos um pequeno script de cliente para testar as chamadas para o conjunto de serviços, mas lembre-se de que o código do cliente pode ser usado facilmente em qualquer lugar.

Então, vamos começar a construir.

Começando

Primeiro, vamos garantir que você tenha tudo o que precisa para executar o corretor e o serviço. Primeiro, comece baixando e instalando Node.js, ZeroMQ e Git em sua máquina. Se você estiver usando OSX, existem pacotes homebrew para cada um deles, e a maioria das distribuições Linux também possui um pacote para cada um deles, então você não deve ter problemas com isso. Os usuários do Windows podem simplesmente usar os links de download fornecidos acima.

Executando o corretor

Depois de instalar todas as dependências necessárias, vamos colocar nosso broker em execução. Neste guia, estamos usando uma implementação Node.js do broker que faz parte do ZMQ Service Oriented Suite. Você pode encontrar seu código e documentação no GitHub. Para executar o broker, primeiro clone um bootstrap do Broker em sua máquina. Este repositório é um bootstrap para usar a biblioteca do broker acima. Observe que esta etapa não é necessária, pois a biblioteca original é executável, mas a diferença entre as duas é que no repositório de bootstrap você pode alterar as configurações padrão.

Então, primeiro, use o seguinte comando Git para baixar o projeto para sua máquina:

 $ git clone [email protected]:dadah/zmq-broker-bootstrap.git

Depois de ter feito isso, vá para o diretório criado:

 $ cd zmq-broker-bootstrap

Agora instale as dependências do pacote:

 $ npm install

O corretor agora está pronto. Para executar seu broker, execute o seguinte comando:

 $ bin/zss-broker run

Você pode encontrar arquivos de configuração para cada ambiente no diretório config/ . Esta é a configuração de desenvolvimento padrão:

 { "broker": { "backend": "tcp://127.0.0.1:7776", "frontend": "tcp://127.0.0.1:7777" }, "log": { "consolePlugin": { "level": "debug" } } }

O parâmetro backend define o endereço ip:port do back-end e front-end do broker. O endereço de back-end é onde o broker recebe solicitações e responde aos serviços, e o endereço de front-end é onde ele recebe e envia para os clientes do serviço. Você também pode definir o nível de log alterando o log.consolePlugin.level . Os valores possíveis são trace , debug , info , warn e error , e eles determinam a quantidade de saída do processo do broker de informações de log.

Executando o serviço

Depois de ter seu corretor ativado, é hora de desenvolver seu primeiro microsserviço Ruby. Comece abrindo uma nova janela do console. Em seguida, crie um diretório onde seus serviços serão armazenados e vá para esse diretório. Neste guia, estamos usando o cliente e serviço Ruby do ZMQ SOA Suite. Há um serviço bootstrap “Hello world” disponível, então vamos usá-lo para executar nosso primeiro microsserviço.

Vá para o diretório de serviços e clone o repositório bootstrap:

 $ git clone [email protected]:dadah/zmq-service-suite-ruby-bootstrap.git

Vá para o diretório recém-criado:

 $ cd zmq-service-suite-ruby-bootstrap

Agora instale todas as dependências:

 $ bundle install

Para iniciar o serviço, execute o seguinte comando:

 $ bin/zss-service run

Excelente. Você tem seu primeiro serviço instalado e funcionando.

Se você for para a janela do console onde deixou seu broker em execução, poderá ver a seguinte saída:

 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

Esse log significa que o broker reconheceu a existência de um novo serviço e está recebendo mensagens de pulsação dele. A cada segundo, o serviço envia uma mensagem de pulsação ao broker, para que ele saiba que a instância do serviço está ativa.

Consumindo do serviço

Então agora temos um serviço em execução, como podemos usá-lo?

No repositório bootstrap, há um cliente fictício que você pode usar para testar seu serviço “Hello World”. Basta abrir uma nova janela ou guia do console e acessar o diretório de serviço. Quando estiver lá, execute o seguinte comando:

 $ bin/zss-client

Você deve ver algo assim:

 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"

Se você for para a janela do console onde seu serviço está sendo executado, você deverá ver isto:

 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

Boa. Você acabou de lançar e consumir seu microsserviço “Hello World”. No entanto, não é isso que nos propusemos a fazer. Queremos construir nosso(s) serviço(s). Vamos a isso, então.

Construindo seu serviço

Primeiro, vamos parar nosso serviço “Hello World”. Vá para a janela do console do serviço e pressione Ctrl+C para interromper o serviço. Em seguida, precisamos transformar nosso serviço “Hello World” no serviço “Person”.

Estrutura do código

Vamos começar dando uma olhada na árvore de código do projeto. Se parece com isso:

A hierarquia de arquivo/pasta de nosso projeto zmq-service-suite-ruby-bootstrap de exemplo. Ele está descrito em detalhes abaixo, mas observe que os últimos três arquivos .rb mencionados estão na verdade em lib/repositories, não em lib em si.

  • O diretório bin é onde você armazena os scripts que iniciam seu serviço.
  • O diretório config armazena todos os arquivos de configuração.
    • O arquivo boot.rb é onde você pode adicionar todas as suas dependências de serviço. Se você abri-lo, poderá notar que existem muitas dependências já listadas lá. Se você precisar adicionar mais, é aqui que você deve fazê-lo.
    • O arquivo application.yml armazena todas as configurações do seu aplicativo. Vamos dar uma olhada neste arquivo mais tarde.
    • No diretório config/initializers você pode adicionar seus scripts inicializadores. Você pode, por exemplo, adicionar configurações para conexões ActiveRecord ou Redis aqui. Os scripts que você adicionar a esse diretório serão executados na inicialização do serviço.
  • No diretório db/migrate você pode armazenar suas migrações do ActiveRecord ou Sequel, se houver. Caso não o faça, você pode excluir este diretório completamente.
  • O diretório lib é onde reside o código principal do aplicativo.
    • O arquivo settings.rb simplesmente carrega o arquivo application.yml e o disponibiliza em todo o escopo do serviço, para que você possa acessar suas configurações em qualquer lugar. Por exemplo, Settings.broker.backend retorna o endereço de back-end do broker que você definiu no arquivo YML acima.
    • O arquivo service_register.rb é onde você registra seus serviços e rotas de serviço. Vamos explicá-lo mais tarde.
    • O arquivo hello_world_service.rb define os terminais do serviço “Hello World”.
    • O diretório lib/daos é onde você armazena seus objetos ActiveModel se estiver usando ActiveRecord, ou qualquer outro objeto de acesso a dados que você possa eventualmente criar, como seus modelos Sequel.
    • O diretório lib/dtos armazena seus objetos de transferência de dados. Esses objetos são os que eventualmente são enviados de volta aos clientes do serviço.
    • O diretório lib/repositories armazena seus repositórios. Repositórios são objetos que permitem que serviços acessem dados e são os únicos objetos com permissão para manipular DAOs. Portanto, se um serviço quiser um grupo de instâncias “Hello World”, ele solicitará ao repositório por elas. O repositório, por sua vez, usa os DAOs apropriados para buscar os dados relevantes do banco de dados. Os dados são então mapeados em uma coleção DTO “HelloWorld” ou DTO “HelloWorld” adequada que é retornada ao serviço.
    • O diretório lib/repositories/mappers é onde você armazena seus mapeadores. Mapeadores são objetos que convertem DAOs em DTOs e vice-versa.

O arquivo application.yml do diretório config tem esta aparência:

 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

Essa configuração simplesmente define o endereço de back-end e front-end do broker e o nível de log.

Se tudo isso parece confuso até agora, não se preocupe, pois ficará mais claro à medida que avançamos.

Atendimento “Pessoal”

Então, vamos continuar com o nosso serviço “Pessoal”. Vamos começar configurando a conexão com o banco de dados. Abra o arquivo config/initializers/active_record.rb e descomente a única linha ali. Em seguida, adicione a seguinte entrada à sua configuração de desenvolvimento no application.yml para que fique assim:

 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

Agora que você adicionou sua configuração de banco de dados, você deve criar o banco de dados. No momento, não há como fazer isso automaticamente, a menos que você esteja usando um banco de dados PostgreSQL padrão, caso em que você pode simplesmente executar:

 $ rake db:create

Se você preferir outro banco de dados, você deve adicionar a gem apropriada ao gemfile e então instalar o projeto em pacote.

Em seguida é a migração. Para isso, basta criar o arquivo db/migrate chamado 000_creates_persons.rb :

 $ touch db/migrate/000_creates_persons_table.rb

Abra o arquivo e crie a migração como faria com uma migração regular do Rails:

 class CreatesPersons < ActiveRecord::Migration def change create_table :persons do |t| t.name t.timestamps end end end

A seguir, execute-o:

 $ 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) =======================================

Agora que criamos nossa tabela, vamos criar um modelo para ela. Crie o arquivo lib/daos/person.rb :

 $ touch lib/daos/person.rb

Edite assim:

 module DAO class Person < ActiveRecord::Base end end

Aí está o seu modelo. Agora você precisa criar um modelo DTO para uma “Pessoa” para que possa devolvê-lo ao cliente. Crie o arquivo lib/dtos/person.rb :

 $ touch lib/dtos/person.rb

Edite assim:

 module DTO class Person < Base attr_reader :id, :name end end

Em seguida, você precisa criar um Mapper para converter o DAO “Person” em um DTO “Person”. Crie o arquivo lib/repositories/mappers/person.rb e edite-o assim:

 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

Aqui, Mapper::Base requer que você implemente self.to_dao e self.to_dto . Se você não quiser fazer isso, você pode implementar self.map em vez disso e substituir o Mapper::Base.map que chama to_dao ou to_dto , dependendo se o atributo que ele recebe é um DAO ou um DTO.

Agora você tem um DAO para acessar seu banco de dados, um DTO para enviá-lo ao cliente e um Mapper para converter um no outro. Agora você pode usar essas três classes em um repositório para criar a lógica que permite obter pessoas do banco de dados e retornar uma coleção correspondente de DTOs.

Vamos criar o repositório então. Crie o arquivo lib/repositories/person.rb :

 $ touch lib/dtos/person.rb

Edite assim:

 module Repository class Person < Repository::Base def get DAO::Person.all.map do |person| Mapper::Person.map(person) end end end end

Este repositório tem apenas o método de instância get , que simplesmente obtém todas as pessoas do banco de dados e as mapeia em uma coleção de DTOs de pessoas - bem simples. Vamos juntar tudo isso agora. Tudo o que resta agora é criar o serviço e o endpoint que chama esse repositório. Para isso, vamos criar o arquivo lib/person_service.rb :

 $ touch lib/person_service.rb

Edite assim:

 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

O serviço “Person” inicializa o repositório em seu inicializador. Todos os métodos de instância pública do serviço “Person” têm carga útil e cabeçalhos que você pode omitir se não precisar deles. Ambas são instâncias do Hashie::Mash e armazenam as variáveis ​​enviadas ao endpoint, seja como atributos ou cabeçalhos, e suas respostas imitam as respostas HTTP, pois cada resposta possui um código de status que os clientes podem usar para descobrir o resultado das solicitações enviadas ao service, juntamente com a carga útil de resposta do serviço. Os códigos de resposta são os mesmos que você esperaria de um servidor HTTP. Por exemplo, uma solicitação bem-sucedida retornará um código de status 200, juntamente com a carga útil da resposta. Se ocorrer algum erro de serviço, o código de status será 500, e se houver algo errado com os parâmetros enviados ao servidor, o código de status será 400. O serviço pode responder com a maioria dos códigos de status HTTP junto com sua carga útil. Portanto, se, por exemplo, você deseja que seu serviço informe seus clientes quando eles não têm permissão para acessar um determinado endpoint, você pode fazê-lo respondendo com um código 403. Você pode ver outro exemplo de códigos de resposta se analisar nosso código de serviço acima. No endpoint get , estamos retornando o código de status 404 junto com a mensagem opcional “Nenhuma pessoa aqui” quando nenhuma pessoa é encontrada, assim como um servidor HTTP retornaria um 404 se não houvesse recursos disponíveis. Se o repositório realmente retornar pessoas, o serviço serializará os DTOs e os retornará ao cliente. Cada DTO tem um serializador padrão que retorna um objeto JSON com as chaves e os valores correspondentes definidos como attr_reader ou attr_accessible na definição do DTO. Você pode, é claro, substituir o serializador definindo seu método serialize em suas classes DTO.

Agora que temos um serviço definido, precisamos registrá-lo. Este é o passo final. Abra o arquivo lib/service_register.rb e substitua todas as ocorrências de “HelloWorld” por “Person”, para que o arquivo finalmente fique assim:

 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 você provavelmente notou, há uma pequena mudança na chamada add_route . Removemos a string “HELLO/WORLD”. Isso ocorre porque a string só é necessária se o verbo de serviço não corresponder ao método que a implementa. No nosso caso, ao chamar o serviço person com o verbo GET, o método a ser chamado é get , então podemos omitir a string.

A classe ServiceRegister é onde você deve definir o método self.get_service . Esse método inicializa o serviço e o conecta ao backend do broker. Em seguida, ele corresponde as rotas desse serviço a métodos em uma ou mais definições de serviço. Por exemplo, no caso a seguir, ele cria o serviço e o vincula ao broker:

 config = Hashie::Mash.new( backend: Settings.broker.backend ) service = ZSS::Service.new(:person, config)

Em seguida, ele instancia um manipulador de serviço:

 personInstance = PersonService.new

Em seguida, o manipulador de serviço é vinculado ao serviço:

 service.add_route(personInstance, :get)

Por fim, deve retornar a instância de serviço.

 return service

Agora, há apenas um último passo antes de podermos lançar nosso serviço “Pessoa”; precisamos criar um script executável para ele. Já temos um para o “HelloService”. Então, abra o arquivo bin/zss-service , substitua “hello-word” por “person” e salve o arquivo. Volte para o console e execute:

 $ 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'

É isso. Você acabou de iniciar seu serviço "Pessoa" pela primeira vez. Agora vamos testá-lo. Abra o arquivo bin/zss-client , altere a variável sid para “person” e altere a chamada do cliente de hello_world() para get() . Feito isso, execute o cliente em uma nova janela:

 $ 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 você pode ver, você capturou um ZSS::Error . Isso ocorre porque geramos um erro quando nenhuma pessoa é encontrada pelo serviço e ainda não temos pessoas no banco de dados do nosso serviço.

Vamos lidar com esse erro então. Abra zss-client e edite-o assim:

 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

Agora estamos imprimindo a mensagem de erro quando o código de erro é 404, enquanto geramos o erro se for diferente. Vamos vê-lo em ação executando nosso cliente novamente:

 $ bin/zss-client "No people here"

Excelente. Vamos agora adicionar algumas pessoas à nossa tabela e ver se elas são devolvidas pelo serviço ao nosso cliente. Para fazer isso, basta abrir um console de serviço:

 $ rake service:console

Adicione algumas pessoas:

 $ 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

Agora, execute seu cliente novamente.

 $ bin/zss-client [{"id"=>1, "name"=>"John"}, {"id"=>2, "name"=>"Mary"}, {"id"=>3, "name"=>"Francis"}]

Aí está.

Considerações Finais

Percorrendo o código fornecido neste guia, você pode pensar que há muitas etapas desnecessárias, como criar repositórios ou DTOs, e você estaria certo. Tudo o que você precisaria para ter um serviço “Person” em funcionamento seria sua classe de serviço e seu DAO, que você poderia chamar diretamente da classe de serviço. No entanto, é uma boa prática seguir o padrão descrito neste artigo, pois permite manter a lógica de serviço separada de sua manipulação de armazenamento de dados. Os serviços devem se concentrar apenas em sua lógica e os repositórios devem lidar com todas as interações com seu armazenamento de dados. Os DTOs determinam as cargas úteis e a serialização de seus serviços, e os DAOs se preocupam apenas em obter dados do armazenamento. As convenções e técnicas descritas neste guia são conhecidas como padrão de repositório, que você pode conferir na imagem abaixo.

O padrão de repositório. A caixa mais à esquerda é "lógica de negócios do cliente", que persiste e consulta a caixa do meio, que é uma pilha que consiste em "mapeador de dados" sobre "repositório" sobre "objeto de consulta", mas separado por linhas pontilhadas. Persistir e consultar são unidos por conexões de caixas externas rotuladas como "entidade de negócios". Finalmente, a caixa mais à direita, "fonte de dados", tem uma seta apontando para "mapeador de dados" e setas bidirecionais com "objeto de consulta".

Gostaria de terminar pedindo a todos que acharam isso útil para contribuir com o pacote de serviços SOA, expandindo-o e aprimorando-o de alguma forma. Todos os seus forks e pull requests são bem-vindos.

Espero que isso ajude você a começar em microsserviços. Se você quiser conferir o código do serviço, uma versão completa está disponível no GitHub.