Fundamentos para sistemas com arquiteturas REST

Este capítulo possui uma versão mais recente:

Não projete APIs para serem RESTful, projete-as para terem as propriedades que você necessita!
Roy Fielding
APIs tem importância crescente no design de aplicações. Boas APIs facilitam o estabelecimento de parcerias, permitindo que sistemas se comuniquem de maneira sólida, facilitando a automação dos negócios.
0
Você concorda?x

O projeto de APIs, em consequência dessa importância, é atividade fundamental de arquitetura. A criação de APIs intuitivas e fáceis é essencial. É essencial que o arquiteto responsável garanta a seleção do estilo mais adaptado as necessidades.
0
Você concorda?x

Desde o início dos anos 2000, vem ganhando destaque APIs projetadas conforme o estilo arquitetural REST, embora, infelizmente, as implementações não sejam, muitas vezes, “fiéis” aos princípios estabelecidos nesse estilo.
0
Você concorda?x

Buscando referências práticas

Este capítulo não visa ser um guia de referência de soluções REST, mas sim, fundamento para pensamento arquitetural.

Há uma pequena lista de “receitas de bolo” para problemas REST no Apêndice A.

Conceito fundamental

Com frequência, APIs que utilizam verbos padrões HTTP – GET, PUT, POST e DELETE – e entregam conteúdo em XML ou JSON são designadas como RESTful. Entretanto, estas características não são suficientes para tal designação.
0
Considerações?x

REST, acrônimo para Representational State Transfer, é um estilo arquitetural para sistemas hipermídia distribuídos. Ele foi introduzido e definido, no ano 2000, por Roy Fielding, em sua tese de doutorado. Trata-se de um conjunto de restrições arquiteturais aplicados a World Wide Web.

Apenas sistemas desenvolvidos observando corretamente o estilo REST podem ser identificados como RESTful

Null Style

Em sua tese, Roy Fielding, defende a existência de duas abordagens para a elaboração de um design arquitetural.

A primeira abordagem é aquela onde um projeto de sistema começa “do zero” – como que em uma “tela em branco” – e vai sendo elaborado utilizando elementos familiares até que, em dado momento, satisfaça as necessidades pretendidas.

A segunda abordagem parte de um sistema “pronto”, com todos os elementos demandados para atender uma determinada necessidade já presentes, porém sem restrições. Então, restrições são aplicadas para “aparar” os elementos do sistema até que ele satisfaça apenas a um conjunto de especificações bem determinado. Este “ponto de partida”, sem restrições, é designado por Roy Fielding como Null Style.

REST, como estilo arquitetural, tem como Null Style World Wide Web.

Sistemas RESTful destacam-se por terem atributos de qualidade como portabilidade, escalabilidade, visibilidade (facilidade para monitoramento), confiabilidade e desempenho percebido (responsividade). Além disso, tem ótimo evolvability.
0
Algo mais a destacar?x

World Wide Web

A World Wide Web (WWW) é um sistema de informação onde documentos e outros recursos são identificados por Uniform Resource Locators (URLs, como https://example.com/), interligáveis por hiperlinks, acessíveis ​​pela Internet.

Os recursos da Web são transferidos por meio de implementações de protocolos, como HTTP, e podem ser acessados ​​pelos usuários por aplicativos navegadores  e publicados por um aplicativos servidores.

O cientista inglês Sir Timothy Berners-Lee inventou a World Wide Web em 1989. Ele escreveu o primeiro navegador da web em 1990 enquanto trabalhava no CERN (European Organization for Nuclear Research) perto de Genebra, Suíça. O navegador foi lançado fora do CERN para outras instituições de pesquisa a partir de janeiro de 1991, e depois para o público em geral em agosto de 1991. A Web começou a entrar no uso diário em 1993, quando sites de uso geral começaram a se tornar disponíveis.

Definindo “recurso”

Para entender REST é fundamental assimilar o conceito de “recursos”. A RFC 2396, de 1998, define “recurso” como:

Um recurso pode ser qualquer coisa que tenha identidade. Os exemplos familiares incluem um documento eletrônico, uma imagem, um serviço (por exemplo, “previsão do tempo de hoje para Los Angeles”) e uma coleção de outros recursos. Nem todos os recursos são “recuperáveis” pela rede; por exemplo, seres humanos, empresas e livros encadernados em uma biblioteca também podem ser considerados recursos. O recurso é o mapeamento conceitual para uma entidade ou conjunto de entidades, não necessariamente a entidade que corresponde a esse mapeamento em qualquer instância específica no tempo. Assim, um recurso pode permanecer constante mesmo quando seu conteúdo — as entidades às quais atualmente corresponde — muda ao longo do tempo, desde que o mapeamento conceitual não seja alterado no processo.

URI e URL

URI, acrônimo para Uniform Resource Identificator, é uma identificação simples para um recurso. Enquanto isso, URL, acrônimo para Uniform Resource Locator, especifica também como este recurso deve ser acessado.

Todas URLs são URIs, mas nem toda URI é uma URL.

Motivações para REST

Embora seja comum associar REST ao desenvolvimento de APIs, suas motivções são muito mais ambiciosas. REST nasceu como uma abordagem para orientar a evolução da arquitetura da Web!

Desde 1994, o estilo arquitetônico REST tem sido usado para guiar o design e o desenvolvimento da arquitetura para a Web moderna. (Roy Fielding)

Em sua teste, Roy Fielding descreve, também, um combinado de lições aprendidas com a aplicação de REST durante a criação dos padrões da Internet para o protocolo de transferência de hipertexto (HTTP) e identificadores de recursos uniformes (URI), as duas especificações que definem a interface genérica usada por todas as interações de componentes na Web, como bem como da implantação dessas tecnologias na forma da biblioteca cliente libwww-perl, o Projeto de servidor HTTP Apache e outras implementações de padrões de protocolo.

A motivação para desenvolver REST foi criar um modelo de arquitetura de como a Web deveria funcionar, de forma que pudesse servir como a estrutura de orientação para os padrões de protocolo da Web. O REST foi aplicado para descrever a arquitetura da Web desejada, ajudar a identificar problemas existentes, comparar soluções alternativas e garantir que as extensões de protocolo não violem as principais restrições que tornam a Web bem-sucedida. Este trabalho foi feito como parte dos esforços do Internet Engineering Taskforce (IETF) e do World Wide Web Consortium (W3C) para definir os padrões de arquitetura para a Web: HTTP, URI e HTML.

Filosofia REST

O pleno entendimento de REST começa pela interpretação do que o acrônimo significa. REpresentational State Transfer literalmente expressa a intencionalidade do estilo arquitetural: transferência de estado através de representações.

As tranferências de estado, em sentido amplo, não se restringem apenas pelo conjunto de dados associados a um recurso, mas também às operações que este recurso suporta. Os diversos componentes computacionais que formam uma solução REST “transferem estado” entre si, afim de preservar consistência.

A transferência de estado entre componentes, coloca os recursos como as “estrelas da festa”, ou seja, o centro de qualquer interação. Afinal, em sentido amplo, trasnferências de estado acontecem pela criação, modificação, recuperação e remoção de recursos, orquestradas por um componente em outro remoto.

O conjunto limitado e padronizado de operações que transferem estado conferem a arquireturas RESTful simplicidade. Ou mesmo conjunto uniforme de operações pode ser aplicado a qualquer recursos, de maneira consistente, através de um conjunto também bem delimitado de verbos HTTP.

A transferência mais óbvia de estado acontece quando um componente solicita a outro a representação correspondente a uma versão, geralmente o “momento atual”, de um recurso, utilizando para isso o verbo HTTP GET.

Menos evidentes são as transferências de estado que acontecem quando um componente envia estado – representações de um recurso – para outro, geralmente com verbos HTTP POST e HTTP PUT.

Finalmente, também são transferências de estado atualizações parciais de recursos, geralmente aplicadas com verbos HTTP POST e HTTP PATCH, bem como operações de exclusão de recursos realizadas com HTTP DELETE.

Elementos arquiteturais para REST

O estilo arquitetural REST define abstrações para os elementos arquitetônicos em um sistema hipermídia distribuído, enquanto ignora os detalhes de implementação.

Sistemas RESTful geralmente serão compostos por componentes, conectores que ligam esses componentes e os dados que eles utilizam.

Elementos arquiteturais relacionados com “Dados”

Sistemas RESTful se caracterizam pela movimentação dos dados através de componentes de processamento, em oposição a outros estilos onde os diversos componentes operam sobre uma única “fonte da verdade”. Essa característica, reforça a coesão, eventualmente comprometendo a eficiência.

Ao projetar dados, um componente “servidor” pode-se adotar uma das seguintes três estratégias:

  1. renderizar os dados para uma representação específica;
  2. enviar os dados em sua representação nativa, acompanhados de código que realiza a renderização para uma representação específica;
  3. enviar os dados em sua representação nativa, acompanhados de metadados para que o componente “cliente” adote uma estratégia de processamento;

São elementos arquiteturais relacionados com dados:

  • recurso;
  • identificador, geralmente uma URI ou URN;
  • representações, como HTML, JSON e XML;
  • metadados da representação, como cabeçalhos como media-type last-modified
  • metadados do recurso, com cabeçalhos como alternates vary
  • metadados de controle, com cabeçalhos como if-modified-since cache-control

Elementos arquiteturais “Conectores”

Os conectores são as tecnologias responsáveis por acessar e transportar os dados entre dois ou mais componentes. Basicamente, eles podem ser agrupados em tecnologias clientes, servidoras, para caching, transporte (tunnel) e resolver (ex: servidores DNS).

Os principais tipos de conectores são “cliente” e “servidor”. A diferença essencial entre os dois é que um cliente inicia a comunicação fazendo uma requisiçã0, enquanto um servidor escuta as conexões e responde às requisições para fornecer acesso aos seus serviços. Eventualmente, um mesmo componente pode incluir conectores de cliente e de servidor.

Um terceiro tipo de conector, o conector de “caching”, pode ficar localizado próximo do “cliente” ou ou do “servidor” a fim de salvar respostas armazenáveis em cache para interações atuais para que possam ser reutilizadas para interações posteriores. Um cache pode ser usado por um cliente para evitar a repetição da comunicação da rede, ou por um servidor para evitar a repetição do processo de geração de uma resposta, com ambos os casos servindo para reduzir a latência de interação.

Elementos arquiteturais “Componentes”

Os componentes são elementos de processamento que se conectam a outros por meio de conectores. Os componentes mais comuns são o “cliente” e o “servidor”. Em um modelo em camadas, um mesmo componente pode ser, ao mesmo tempo, “cliente” e “servidor” realizando algum tipo de transformação ou segurança.

Sistemas RESTful são “cliente-servidor”

A primeira restrição definida for Fielding é que sistemas que seguem o estilo arquitetural REST sejam implementações cliente-servidor. Cabe ao componente “cliente” disponibilizar interfaces com o usuário. Enquanto isso, componentes “servidor” fornecem dados. Em sistemas RESTful, eventualmente, um mesmo componente de software poderá operar como cliente e servidor.

Um software navegador utilizado para navegar na internet atua como cliente. Afinal, envia requisições HTTP, para um servidor, de forma a acessar e manipular dados.

A separação de responsabilidades, segundo Fielding, reforça atributos de qualidade como portabilidade – permitindo que implementações “cliente” independentes sejam realizadas em diversas plataformas – e a escalabilidade – simplificando os componentes “servidor”. Além disso, por habilitar que “cliente” e “servidor” evoluam de forma independente, colaboram para o evolvability.

Componentes “servidor” são stateless

A segunda restrição definida por Fielding é que sistemas RESTful sejam stateless. Ou seja, cada requisição de componentes “cliente”  deve conter todas as informações contextuais necessárias para serem atendidas, sem depender de qualquer dado de contexto armazenado no componente “servidor”. Assim, informações de sessão devem ser mantidas, inteiramente, apenas no componente “cliente”.

Por serem stateless, sistemas RESTful têm melhor visibilidade, confiabilidade e escalabilidade. Afinal:

  • cada solicitação pode ser verificada, completamente, de maneira isolada, já que contem todos os dados necessários para a análise;
  • por não manterem estado de contexto, é mais fácil recuperar o “lado servidor” de falhas parciais;
  • por não haver necessidade de manter estado de contexto, sistemas RESTful são mais fáceis de implementar e de escalar.

Como pontos potencialmente negativos, a restrição de que componentes “servidor” sejam stateless implica em tráfego de dados maior na rede, pelo transporte da informação de estado contexto. Além disso, há potencial excesso de autonomia dos componentes “cliente” (que operam sem supervisão do servidor).

Recursos devem ser indicados como sendo “cacheáveis” ou “não-cacheáveis”

A terceira restrição definida por Fielding é que sistemas RESTful devem indicar a possibilidade de adoção de caching nos componentes “cliente”. Para isso, todas as respostas do componente “servidor” devem ser identificadas, implícita ou explicitamente, como armazenáveis ou não armazenáveis em cache.

A adoção de caching melhora a eficiência, colaborando para a escalabilidade e desempenho. Entretanto, dados armazenados em cache podem ficar desatualizados, antes de serem invalidados, comprometendo a confiabilidade.

Comunicações entre “cliente” e “servidor” devem ocorrer através de interface uniforme

A quarta restrição definida por Fielding é que as comunicações entre componentes “cliente” e “servidor”, em sistemas RESTful, ocorram através de interfaces uniformes (consistentes). Dessa forma, a implementação dos componentes “cliente” e “servidor” podem ocorrer de forma desacoplada (e independente).


Há quatro restrições arquiteturais adicionais derivadas da decisão de adotar interfaces uniformes:

  1. Recursos devem ser identificados consistentemente. Na prática, recursos em um sistema RESTful devem estar associados a identificações estáveis, ou seja, que não mudam durante as interações, tampouco quando o estado interno do recurso (conjunto de dados que o compõe) é alterado.
  2. Recursos devem ser manipulados a partir de representações. A representação de um recurso é composta por uma sequência de bytes e metadados que a descrevem. Opcionalmente, uma representação também contém metadados que descrevem o recurso em si. Em implementações REST usando HTTP, por exemplo, entidades de domínio são recursos que podem ser expostos em representações usando XML, JSON e mais; enquanto isso, metadados descrevendo uma representação são fornecidos no cabeçalho;
  3. Mensagens devem ser auto-descritivas. Ou seja, devem conter todas as informações para que o receptor – cliente ou servidor – tenha condições realizar interpretação correta, sem necessitar de mensagens adicionais ou documentação em separado.
  4. Hipermídia deve ser utilizada como mecanismo de estado (HATEOAS). Utilizando hiperlinks e, possivelmente, modelos consistentes de URI, como parte dos metadados que descrevem o recurso, seja para descrever documentos ligados como ativações de estado.

HATEOAS

Hypermedia as the Engine of Application State, ou HATEOAS, é uma “maneira” de implementar APIs REST utilizando hipermídia para indicar que ações ou navegações estão disponíveis para um determinado recurso. Estas  ações e a navegação são derivadas do estado do recurso e, eventualmente, da própria API. Elas são disponibilizadas para o cliente através de uma coleção de links.

{
  “custormerId”: 1, 
  “firstName”: “John”, 
  “lastName”: “Doe”, 
  ...
  
  links: []
}

Não há uma definição universalmente aceita sobre onde esta coleção de links deve ser fornecida. Há quem defenda a inclusão na representação do recurso. Outros, defendem a utilização dos cabeçalhos da “response HTTP”. Quanto aos dados necessários em cada link, há a RFC (5598).

O link para Self

O primeiro link da coleção deveria ser uma referência para o próprio objeto.

{
  “custormerId”: 1, 
  “firstName”: “John”, 
  “lastName”: “Doe”, 
  ...
  
  links: [{
    “rel”: “self”,
    “href”: “http://mydomain/customers/1” 
  }]
}

Dessa forma, caso a representação do recurso “passeie” pela aplicação cliente, ou, caso ela, eventualmente, seja persistida ou compartilhada com outros contextos, em qualquer momento, seria possível determinar sua origem sem “inferências no código”.

Links para as ações suportadas pelo recurso

Imagine-se concebendo uma interface para o usuário. Nela, deve ser possível alterar os dados de um recurso ou solicitar sua exclusão. Eventualmente, alguns recursos (um pedido, por exemplo) não podem ser alterados sob certas condições. Ou ainda, não podem ser excluídos. Como e onde a “lógica” para determinar as ações possíveis deve ser implementada?

{
  “custormerId”: 1, 
  “firstName”: “John”, 
  “lastName”: “Doe”, 
  ...
  
  links: [{
    “rel”: “self”,
    “href”: “http://mydomain/customers/1” 
  },{
    “rel”: “update”,
    “href”: “http://mydomain/customers/1”
  },{
    “rel”: “delete”,
    “href”: “http://mydomain/customers/1”
  }]
}

Fornecer a lista de ações possíveis para um recurso na coleção de links permite que essa lógica seja implementada totalmente no serviço e simplifica muito a implementação da aplicação cliente. O botão “excluir”, por exemplo, estaria disponível apenas se o link correspondente estivesse na coleção.

Outra vantagem de retornar a lista de links com as ações disponíveis é que a lógica pode mudar no servidor, incluindo os endereços, sem alteração alguma no código da aplicação cliente.

Links para navegação

Eventualmente, um recurso retornado pela API possuirá outros recursos relacionados. Por exemplo, em uma aplicação empresarial, um “cliente” possuirá uma coleção de “pedidos” que realizou.

{
  “custormerId”: 1, 
  “firstName”: “John”, 
  “lastName”: “Doe”, 
  ...
  
  links: [{
    “rel”: “self”,
    “href”: “http://mydomain/customers/1”
  },{
    “rel”: “update”,
    “href”: “http://mydomain/customers/1”
  },{
    “rel”: “delete”,
    “href”: “http://mydomain/customers/1”
  },{
    “rel”: “orders”,
    “href”: “http://mydomain/customers/1/orders”
  }]
}

A presença desses links evita que a aplicação cliente precise fazer “inferências” sobre como “chegar” em recursos relacionados, diminuindo acoplamento. Essa ação autoriza, por exemplo, a mudança na estratégia de rotas sem afetar a aplicação cliente.

{
  “custormerId”: 1,
  “firstName”: “John”,
  “lastName”: “Doe”,
  ...
  links: [ ...,
  {
    “rel”: “orders”,
    “href”: “http://mydomain/orders?customerId=1” ]
   }]
}

REST não é CRUD

Há uma confusão frequente entre os conceitos de transferências de estado, através de representações, e operações CRUD. Não é incomum ver, por exemplo, a associações de CREATE com o verbo HTTP POST, RETRIEVE com o verbo HTTP GET, UPDATE com HTTP PUT e, finalmente, DELETE com HTTP DELETE. Esta é uma visão ingênua e superficial.
0
Concorda?x

Algumas pessoas pensam que REST sugere não usar POST para atualizações. Pesquise minha dissertação e você não encontrará nenhuma menção a CRUD ou POST. A única menção de PUT é em relação à falta de cache de write-back do HTTP. (Roy Fielding)

Não há restrições quanto a utilizar HTTP POST para atualizações

Uma das “verdades absolutas” sobre REST, que não é verdade, é a ideia de que o verbo HTTP POST não pode ser utilizado para atualizar recursos. Aliás, o próprio Roy Fielding é bastante opinativo a este respeito.

A única coisa que o REST exige dos métodos é que eles sejam definidos uniformemente para todos os recursos (ou seja, para que os intermediários não precisem saber o tipo de recurso para entender o significado da solicitação). Enquanto o método está sendo usado de acordo com sua própria definição, REST não tem muito a dizer sobre isso.

Por exemplo, não é RESTful usar GET para executar operações inseguras porque isso violaria a definição do método GET em HTTP, o que, por sua vez, enganaria intermediários e spiders. Não é RESTful usar POST para recuperação de informações quando essas informações correspondem a um recurso potencial, porque esse uso impede a reutilização segura e o efeito de rede de ter um URI. Mas por que você não deve usar o POST para realizar uma atualização? O hipertexto pode informar ao cliente qual método usar quando a ação executada não for segura. PUT é necessário quando não há hipertexto dizendo ao cliente o que fazer, mas a falta de hipertexto não é particularmente RESTful. (Roy Fielding)

 

Implementações do “servidor” podem ser compostas em camadas

A quinta restrição definida por Fielding é que componentes “servidor” podem ser desenvolvidos a partir de composições que obedecem o estilo de arquitetura em camadas. Ou seja, componentes “cliente” tem acesso apenas a camada mais externa, e esta pode utilizar recursos de camadas inferiores, em diversas representações, para compor novos recursos.

Camadas podem ser utilizadas para encapsular sistemas legados, protegendo tanto novos “clientes” de “servidores” legados, como para proteger novos “servidores”  de “clientes” legados.

Camadas diferentes podem operar, inclusive, em níveis de abstração distintos, prevenindo changing coupling. Por exemplo:

  • em nível mais baixo, expondo “entidades” como recursos nativos de sistemas pesados,
  • em nível intermediário, expondo recursos relacionados a processos de negócio que são composições das “entidades”
  • em nível mais alto, expondo recursos relacionados a “experiência” que são composições dos “processos”.

Finalmente, tecnicamente, camadas também podem ser utilizadas para melhorar a escalabilidade habilitando funcionalidades como balanceamento de carga e represamento, protegendo sistemas legados não preparados para escalabilidade.

Componentes “servidor” podem fornecer code-on-demand para “clientes”

A sexta, e última, restrição indicada por Fielding é que componentes “servidor” podem fornecer code-on-demand para componentes “cliente”. Ou seja, em sistemas RESTful componentes “servidor” podem extender funcionalidades de componentes “cliente” através do download de código executável na forma de applets ou scripts. Teoricamente, isso deve simplificar a implementação de features comuns com implementações pré-implementadas.

O modelo de maturidade de Leonard Richardson (RMM)

Leonard Richardson, famoso autor de livros sobre REST, propôs um modelo de quatro categorias de compatibilidade (melhor do que maturidade)  com REST. Trata-se Richardson Maturity Model (RMM)

O primeiro nível proposto por Richardson (level zero) é dedicado aos serviços que não fazem uso apropriado de identificadores, tampouco dos verbos HTTP ou HATEOAS. São exemplos de APIs nesse nível aquelas desenvolvidas usando XML-RPC.

Serviços no segundo nível (level one), segundo o RMM, utilizam URIs de maneira consistente, porém apenas com um único verbo HTTP (geralmente POST).

No terceiro nível (level two), estão os serviços que já fazem uso adequado de URIs e também semanticamente ajustado dos verbos HTTP. Trata-se da maioria das implementações REST-like. Finalmente, serviços no quarto nível (level three) tem recursos implementando HATEOAS.

Conforme Roy Fielding, apenas serviços no quarto nível no RMM são RESTful.

Conway, outra vez!

O desenvolvimento de sistemas RESTful preconiza clara separação das equipes em desenvolvedores de componentes “cliente” e componentes “servidor”.

Quando o modelo “em camadas” de componentes “servidor” é adotado, é comum, que cada camada seja mantida por um time dedicado. Aliás, em casos extremos, cada componente “servidor”, em cada camada, poderá ser mantido por um time especialista.

Eventualmente, um time de operações poderá ser estabelecido para projeto, implementação e manutenção de conectores. O mesmo também pode ocorrer para manutenção das fontes de dados.

Indicações e contraindicações

APIs REST parecem ser uma unanimidade. Infelizmente, essa “unanimidade” é questionável frente ao fato de que muitas implementações não respeitam alguns de seus princípios.

APIs REST são, seguramente, mais acessíveis e fáceis de usar do que APIs SOAP. Entretanto, é importante destacar que são, fundamentalmente, data-driven. Eventualmente, alguns domínios são melhor expressados  frente a modelos RPC.

// TODO

Antes de avançar para o próximo capítulo, recomendo as seguintes reflexões:

  1. As implementações auto-proclamadas RESTful que você conhece realmente o são?
  2. Em que cenários você não utilizaria REST como estilo arquitetural para desenho de APIs?
  3. Você está habituado a utilizar HATEOAS?

Referências bibliográficas

FIELDING, Roy. Architectural Styles and the Design of Network-based Software Architectures. 2000. 162 f. Tese (Doutorado) – Curso de Information And Computer Science, University Of California, Irvine, Ca, 2000. Disponível em: https://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm. Acesso em: 5 jun. 2021.

FIELDING, Roy. It is OK to use POST. 2009. Disponível em: https://roy.gbiv.com/untangled/2009/it-is-okay-to-use-post. Acesso em: 5 jun. 2021.

FOWLER, Martin. Richardson Maturity Model: steps toward the glory of rest. steps toward the glory of REST. 2010. Disponível em: https://martinfowler.com/articles/richardsonMaturityModel.html. Acesso em: 7 jun. 2021.

Compartilhe este capítulo:

Compartilhe:

Comentários

Participe da construção deste capítulo deixando seu comentário:

Inscrever-se
Notify of
guest
0 Comentários
Feedbacks interativos
Ver todos os comentários

Fundador e CEO da EximiaCo, atua como tech trusted advisor ajudando diversas empresas a gerar mais resultados através da tecnologia.

Mentoria

para arquitetos de software

Imersão, em grupo, supervisionada por Elemar Júnior, onde serão discutidos tópicos avançados de arquitetura de software, extraídos de cenários reais, com ênfase em systems design.

Consultoria e Assessoria em

Arquitetura de Software

EximiaCo oferece a alocação de um Arquiteto de Software em sua empresa para orientar seu time no uso das melhores práticas de arquitetura para projetar a evolução consistente de suas aplicações.

ElemarJúnior

Fundador e CEO da EximiaCo, atua como tech trusted advisor ajudando diversas empresas a gerar mais resultados através da tecnologia.

+55 51 99942-0609 |  contato@eximia.co

+55 51 99942-0609  contato@eximia.co

0
Quero saber a sua opinião, deixe seu comentáriox
()
x