Por que o REST Api não segue o padrão de design do Facade

9

Ao comparar a estrutura REST [api] com um modelo OO, vejo essas semelhanças:

Ambos:

  • Os dados são orientados

    • REST = Recursos
    • OO = Objetos
  • Operação surround em torno de dados

    • REST = surround VERBS (Get, Post, ...) em torno dos recursos
    • OO = promove operação em torno de objetos por encapsulamento

No entanto, boas práticas de OO nem sempre permanecem nas APIs REST ao tentar aplicar o padrão de fachada, por exemplo: no REST, você não possui 1 controlador para manipular todas as solicitações E não oculta a complexidade interna do objeto.

Relação simples de objetos entre 2 conceitos

Analogia entre OO e REST

Pelo contrário, o REST promove a publicação de recursos de todas as relações com um recurso e outro em pelo menos duas formas:

  1. via relações de hierarquia de recursos (um contato do id 43 é composto por um endereço 453): /api/contacts/43/addresses/453

  2. via links em uma resposta json REST:

>> GET /api/contacts/43
<< HTTP Response {
   id: 43, ...
   addresses: [{
      id: 453, ...
   }],
   links: [{
      favoriteAddress: {
          id: 453
      }
   }]
}

Complexidade básica oculta pelo objetoA

Voltando ao OO, o padrão de design da fachada respeita a Low Couplingentre um objetoA e seu ' clienteB de objeto ' e High Cohesionpara esse objetoA e sua composição interna de objetos ( objetoC , objetoD ). Com a interface objectA , isso permite que um desenvolvedor limite o impacto no objeto B das mudanças internas do objeto A (no objeto C e no objeto D ), desde que a API do objeto A (operações) ainda seja respeitada.

No REST, os dados (recurso), as relações (links) e o comportamento (verbos) são explodidos em diferentes elementos e estão disponíveis para a web.

Jogando com o REST, eu sempre tenho impacto nas alterações de código entre meu cliente e servidor: porque eu tenho High Couplingentre minhas Backbone.jssolicitações e Low Cohesionentre recursos.

Eu nunca descobri como deixar meu Backbone.js javascript applicationacordo com a descoberta de " recursos e recursos REST " promovida pelos links REST. Entendo que a WWW deve ser servida por vários servidores e que os elementos OO precisaram ser explodidos para serem atendidos por muitos hosts, mas para um cenário simples como "salvar" uma página que mostra um contato com seus endereços, Eu acabo com:

GET /api/contacts/43?embed=(addresses)
[save button pressed]
PUT /api/contacts/43
PUT /api/contacts/43/addresses/453

o que me levou a mover a responsabilidade transacional atômica da ação de salvar nos aplicativos dos navegadores (já que dois recursos podem ser endereçados separadamente).

Com isso em mente, se eu não puder simplificar meu desenvolvimento (os padrões de design do Facade não são aplicáveis) e se eu trouxer mais complexidade ao meu cliente (manipulando a economia de energia atômica), qual é o benefício de ser RESTful?

Alain
fonte
11
Deixe-me entender. Você está dizendo que precisa atualizar um Contato com um endereço vinculado "incorporado" (composição) usando duas chamadas REST, uma para o Contato e outra para o Endereço. Você tem uma fachada para lidar com a atualização de contatos. Qual é o problema de fazer PUT /api/contacts/43atualizações em cascata para objetos internos? Eu tinha muitas APIs projetadas assim (o URL mestre lê / cria / atualiza o "todo" e os sub-URLs atualizam as peças). Apenas certifique-se de não atualizar o endereço quando nenhuma alteração for necessária (por motivos de desempenho).
Anthony Accioly
@AnthonyAccioly, você entendeu corretamente. Tentei esclarecer minha pergunta adicionando algumas imagens. Sua sugestão é boa e essa é também a conclusão que cheguei: controlar manualmente minha solicitação e usar um objeto incorporado para enviar apenas uma solicitação para manter atômica minha atualização. Ainda: Por que tudo no REST me afasta das qualidades ou imposição de OO (encapsulamento, ...) achatando meu modelo (implicando muitos controladores). Usar sua solução fornece 1 atualização atômica. Não usar sua solução traz ao desenvolvedor uma nova responsabilidade e nenhuma regra na API para impedir que ele faça isso.
Alain
Apenas uma observação: as "relações de hierarquia de recursos" que você mencionou são uma questão de como alguém pode decidir codificar as informações de relacionamento em um identificador (neste caso, uma URL). Não tenho certeza de que essa exposição de informações faça parte do REST ou promova, apenas uma decisão que você faz sozinho ao criar os URLs de um sistema. Se você acredita de outra forma, você tem alguma referência de Roy Fielding discutindo esse assunto como parte do REST?
Thiago Silva

Respostas:

7

Eu acho que os objetos são construídos apenas corretamente em torno de comportamentos coerentes e não em torno de dados. Vou provocar e dizer que os dados são quase irrelevantes no mundo orientado a objetos. De fato, é possível e, em algum momento, comum ter objetos que nunca retornam dados, por exemplo "log sinks" ou objetos que nunca retornam os dados que são transmitidos, por exemplo, se calculam propriedades estatísticas.

Não confundamos o PODS (que é pouco mais que estruturas) e os objetos reais que têm comportamentos (como a Contactsclasse no seu exemplo) 1 .

Os PODS são basicamente uma conveniência usada para conversar com repositórios e objetos de negócios. Eles permitem que o código seja do tipo seguro. Nem mais nem menos. Os objetos de negócios, por outro lado, fornecem comportamentos concretos , como validar seus dados, armazená-los ou usá-los para realizar um cálculo.

Portanto, comportamentos são o que usamos para medir a "coesão" 2 , e é fácil ver que no seu exemplo de objeto há alguma coesão, mesmo que você mostre apenas métodos para manipular contatos de nível superior e nenhum método para manipular endereços.

Em relação ao REST, você pode ver os serviços REST como repositórios de dados. A grande diferença com o design orientado a objetos é que existe (quase) apenas uma opção de design: você tem quatro métodos básicos (mais se contar HEAD, por exemplo) e, é claro, tem muita margem de manobra com os URIs, para que você possa fazer astuciosamente coisas como passar muitos IDs e recuperar uma estrutura maior. Não confunda os dados que eles passam com as operações que executam. Coesão e acoplamento são sobre código e não dados .

Claramente, os serviços REST têm alta coesão (todas as formas de interagir com um recurso estão no mesmo lugar) e baixo acoplamento (todo repositório de recursos não requer conhecimento dos outros).

O fato básico permanece, no entanto, o REST é essencialmente um padrão de repositório único para seus dados. Isso tem consequências, porque é um paradigma construído em torno da fácil acessibilidade em um meio lento, onde há um alto custo para "chattiness": os clientes geralmente desejam executar o mínimo de operações possível, mas ao mesmo tempo recebem apenas os dados de que precisam . Isso determina a profundidade da árvore de dados que você enviará de volta.

No design (correto) orientado a objetos, qualquer aplicativo não trivial fará operações muito mais complexas, por exemplo, através da composição. Você pode ter métodos para realizar operações mais especializadas com os dados - o que deve ser assim, porque, embora o REST seja um protocolo de API, o OOD é usado para criar aplicativos inteiros para o usuário! É por isso que medir a coesão e o acoplamento é fundamental no OOD, mas quase insignificante no REST.

Agora, deveria estar óbvio que analisar o design de dados com conceitos de OO não é uma maneira confiável de medi-lo: é como comparar maçãs e laranjas!

De fato, os benefícios de ser RESTful são (principalmente) os descritos acima: é um bom padrão para APIs simples em um meio lento. É muito armazenável em cache e fragmentável. Possui controle refinado sobre chattiness, etc.

Espero que isso responda à sua pergunta (bastante multifacetada) :-)


1 Esse problema faz parte de um conjunto maior de problemas, conhecido como incompatibilidade de impedância objeto-relacional . Os proponentes das ORMs geralmente estão no campo que explora as semelhanças entre análise de dados e análise de comportamento, mas as ORMs foram criticadas ultimamente porque parecem não resolver realmente a incompatibilidade de impedância e são consideradas abstrações com vazamento .

2 http://en.wikipedia.org/wiki/Cohesion_(computer_science)

Sklivvz
fonte
Você está certo, tive dificuldade em explodir minha pergunta em muitos aspectos, para definir um ponto específico, pois a pergunta aborda uma conclusão "errada" com base no acúmulo desses aspectos. Vou tentar agora responder aos seus pontos em muitos comentários.
Alain
[texto 1] Usei a palavra "dados" para abstrair do mundo OO e REST. Qual palavra você usaria para abstrair propriedades no OO e estrutura de dados no REST?
Alain
@Alain "data" está bem, mas meu objetivo não é confundir PODS e objetos de negócios. Quando falamos sobre OOD, geralmente falamos sobre o segundo. A primeira é uma conveniência e pode ser pensada com a mesma facilidade em um dicionário, estrutura ou tupla.
Sklivvz
Sim, concordo, uso o Backbone.js onde o salvamento de um modelo está usando uma estrutura json simples. É aqui que o texto reflete minha experiência real de codificação.
Alain
[texto 3] Isso é novo para mim. Eu pensei que a coesão fosse medida pelo número de vezes que os métodos usam uma relação específica ... eu prefiro sua maneira de vê-la.
Alain
1

Com isso em mente, se eu não puder simplificar meu desenvolvimento (os padrões de design do Facade não são aplicáveis) e se eu trouxer mais complexidade ao meu cliente (manipulando a economia de energia atômica), qual é o benefício de ser RESTful?

A resposta para "onde está o benefício de ser RESTful?" é cuidadosamente analisado e explicado aqui: http://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm

A confusão nesta pergunta é que não se trata de características do REST e de como lidar com elas, mas assumindo que o design das URLs criadas para o seu sistema de exemplo tem algo a ver com ser RESTful. Afinal, o REST afirma que existem coisas chamadas recursos e um identificador deve ser fornecido para aqueles que precisam ser referenciados, mas não determina que, digamos, as entidades no seu modelo de ER devam ter 1 a 1 de correspondência com os URLs que você criou (nem que os URLs devem codificar a cardinalidade dos relacionamentos de ER no modelo).

No caso de contatos e endereços, você poderia ter definido um recurso que represente essas informações em conjunto como uma única unidade, mesmo que você queira extrair e salvar essas informações em, por exemplo, diferentes tabelas de banco de dados relacional, sempre que forem PUT ou POSTed .

Thiago Silva
fonte
1

Isso porque fachadas são um 'clamor'; você deve dar uma olhada em 'api abstraction' e 'api encadeamento'. A API é uma combinação de dois conjuntos de funcionalidades: E / S e gerenciamento de recursos. Localmente, a E / S é boa, mas dentro de uma arquitetura distribuída (por exemplo, proxy, porta da API, fila de mensagens etc.), a E / S é compartilhada e, portanto, os dados e a funcionalidade ficam duplicados e emaranhados. Isso leva a preocupações arquitetônicas transversais. Isso assola TODAS as APIs existentes.

A única maneira de resolver isso é abstrair a funcionalidade de E / S da API para um manipulador anterior / posterior (como um manipuladorIntercepter no Spring / Grails ou um filtro no Rails) para que a funcionalidade possa ser usada como uma mônada e compartilhada entre instâncias e externos ferramentas. Os dados para solicitação / resposta também precisam ser externalizados em um objeto para que possam ser compartilhados e recarregados também.

http://www.slideshare.net/bobdobbes/api-abstraction-api-chaining

Orubel
fonte
0

Se você entende seu serviço REST ou, em geral, qualquer tipo de API, apenas como uma interface adicional exposta aos clientes para que eles possam programar seu (s) controlador (es) através dele, torna-se subitamente fácil. O serviço nada mais é do que uma camada adicional sobre sua lógica de negócios.

Em outras palavras, você não precisa dividir a lógica de negócios entre vários controladores, como fez na foto acima e, mais importante, não deveria. As estruturas de dados usadas para trocar dados não precisam corresponder às estruturas de dados usadas internamente; elas podem ser bem diferentes.

É de ponta e amplamente aceito que é uma má idéia colocar qualquer lógica de negócios no código da interface do usuário. Mas toda interface do usuário é apenas um tipo de interface (o I na interface do usuário) para controlar a lógica de negócios por trás. Consequentemente, parece óbvio que também é uma má idéia colocar qualquer lógica de negócios na camada de serviço REST ou qualquer outra camada de API.

Conceitualmente falando, não há muita diferença entre a interface do usuário e a API de serviço.

JensG
fonte
Concordo com a noção de camada, mas o que você quer dizer com "programe seu controlador através dela"?
Alain
11
Quero enfatizar o fato de que o próprio controlador é o serviço real. A interface envolvida na coisa toda é apenas um meio de conseguir algo. Existe qualquer interface para facilitar o acesso à funcionalidade agrupada, de uma maneira ou de outra. Uma GUI faz isso para usuários humanos, uma API de serviço é usada pelos clientes. Ambos os públicos-alvo desejam obter algo com as coisas envolvidas na interface. Concordo que "programa" pode não ser a melhor formulação para isso, mas "controlar os controladores" soa estranho quer ;-)
JensG