REST anêmico e DDD

5

Suponha o seguinte modelo de domínio:

Um [Order] consiste em várias propriedades, bem como uma lista de [OrderLine] s.
Para o nosso propósito, uma ordem é bastante parecida com uma lógica de negócios muito pequena.

Considere a seguinte história de usuário: um usuário recupera um pedido. Em seguida, ele exclui uma linha de pedido e adiciona mais 2. Finalmente, ele salva o pedido.

Como isso seria implementado no REST? Eu vejo algumas opções, mas cada uma com algumas desvantagens:

  1. Abordagem de granulação grossa:
    A ação de salvar resultará em uma solicitação PUT (PUT / orders / 3) contendo o pedido inteiro, incluindo as linhas de pedido. A desvantagem é que é difícil descobrir o que mudou (por exemplo, quais linhas de pedido devem ser adicionadas / atualizadas / excluídas?).
    Eu posso ver isso se transformando em uma confusão real rapidamente quando mais tarde acontece que há lógica de negócios específica ao adicionar uma linha de pedido. Basicamente, resume-se a substituir um pedido por outro e manter os dedos cruzados para que tudo seja válido, a menos que decidamos reimplementar as regras de negócios do lado do cliente.

  2. Abordagem refinada:
    A ação da linha de ordem de exclusão resultará em uma solicitação DELETE (DELETE / orders / 3 / lines / 5). A ação de adicionar linha de pedido resultará em uma solicitação POST (POST / orders / 3 / lines). Isso implica que o aplicativo agora precisa acompanhar muitas alterações e executar as alterações na ordem correta quando o usuário decidir salvar. Outra desvantagem é que, quando um novo pedido é criado, ele deve primeiro ser salvo antes que as linhas do pedido possam ser adicionadas.

Como o repouso lida com documentos, não tenho certeza de como isso se traduziria em operações executadas em um modelo de domínio, já que os documentos são anêmicos e os modelos de domínio não.

    
por Bart 08.02.2017 / 03:06
fonte

4 respostas

3

Since rest deals with documents I'm not sure how those would translate into operations executed on a domain model, given that documents are anemic and domain models are not.

A principal coisa a ter em mente é que o ponto de uma API da web é adaptar seu domínio para a web; isto é, para fornecer a ilusão de que seus recursos são realmente apenas entradas em um armazenamento de documentos.

De certa forma, um consumidor deve ser capaz de conduzir sua API usando um editor de documentos com conhecimento da web.

Abordagem de granulação grossa

The save action will result in a PUT request (PUT /orders/3) containing the entire order including order lines. The drawback is that it's hard to figure out what has changed (i.e. which order lines should be added / updated / deleted?).

Essa é uma abordagem perfeitamente razoável: você diz à API como deseja que o recurso seja exibido e a API precisa descobrir como chegar lá.

Como os carrinhos de compras geralmente não exigem colaboração entre vários editores, você pode obter criando um novo modelo de dados para o recurso a partir do instantâneo que recebeu e, em seguida, substituindo o modelo existente.

Se você, por qualquer motivo, precisar descobrir mais mensagens de comando de granulação do diff; sim, isso pode ser estranho. Em um caso como este, não é tão ruim - você pode detectar mudanças com bastante facilidade, e qualquer mudança válida provavelmente é tirada de um pequeno vocabulário.

Também é importante notar que você não precisa suportar PUT em qualquer representação arbitrária do recurso, mas pode escolher uma representação que faça sentido (e, esperançosamente, uma onde é fácil identificar as mudanças corretas ).

Abordagem refinada

The delete order line action will result in a DELETE request (DELETE /orders/3/lines/5).

Qualquer URI que você queira usar é bom no que diz respeito ao REST; mas conceitualmente pode ser melhor pensar em termos de remover a entidade por id, em vez de por posição na lista.

The add order line action will result in a POST request

Também pode ser feito como um PUT.

This implies the app now needs to keep track of a lot of changes, and execute the changes in the correct order when the user decides to save.

Sim; Acredito que você tenha uma situação semelhante com um cliente ocasionalmente conectado. As mensagens de comando são enfileiradas até o sistema de destino ficar disponível.

Outra abordagem a considerar seria manipular o histórico do recurso, em vez do próprio recurso. Efetivamente, a história se torna uma coleção única de alterações.

I'm just wondering how I can avoid duplicating business rules on the client. For example, if an order line cannot be deleted under certain circumstances, nothing prevents the client from doing so.

Há algumas respostas aqui, com base na ideia de que você sinaliza essas informações nas representações.

Uma resposta é que você simplesmente anota que a linha do pedido não deve ser excluída. As regras de negócios que decidem se uma linha de pedido pode ser excluída ao vivo no servidor, o cliente apenas vê que o sinalizador foi definido.

Você pode comunicar essas informações fora da banda para os clientes.

Na abordagem refinada, a resposta usual é que você usa controles hipermídia. Em vez de os clientes enviarem uma mensagem para um terminal pré-determinado, espera-se que o cliente examine a representação e "siga um link". Assim, você pode comunicar o fato de que um item de pedido não pode ser excluído, removendo o link do item de exclusão da representação.

Em uma abordagem específica, suponho que você poderia tentar distinguir as partes modificáveis do recurso e fornecer representações editáveis apenas das partes que o cliente pode editar. Há uma certa confusão lá.

    
por 08.02.2017 / 07:43
fonte
1

Consider the following user story: a user retrieves an order. He then deletes one order line and adds 2 more. Finally, he saves the order.

Fundamentalmente, com um rich client editado e & Salvar capacidade, você está falando sobre uma abordagem de granulação grossa, em que o cliente pode fazer um número arbitrário de ajustes e salva o resultado final. É improvável que o cliente espere que todas as etapas de edição do lado do cliente sejam executadas no servidor ao salvar; É mais provável que eles esperem que o resultado final seja enviado ao servidor.

(Por exemplo, se eles fizerem inserções e exclusões dos mesmos itens antes de salvar, não esperará que a inserção seja verificada pelas regras de negócios, já que foi excluída antes de ser salva.)

Muitos carrinhos de compras não têm uma edição complexa no lado do cliente e, em seguida, salvam a função, portanto, para eles, eles trabalham naturalmente em uma granularidade mais refinada. As edições do carrinho só permitem alterar contagens de itens de linha, por exemplo. Inserções de novos itens de linha não acontecem como edições & excepto do carrinho, mas como uma operação diferente, e. de um produto (página).

Sua interface REST precisa suportar o comportamento do cliente desejado, seja grosseiro, bom (ou ambos com várias interfaces do usuário).

    
por 08.02.2017 / 18:52
fonte
1

Se tudo o que você tem é um martelo, tudo parece um prego.

A solução para o seu problema pode ser usar os "comandos" (como no CQRS e Padrão CQS ).

POST /api/order/188423
{command: "AddOrderLine", "item_id": 123}

Após o envio deste comando, a API retornará o recurso de pedido atualizado. Essa abordagem é mais fácil de entender e mais testável. Ele também permite que você execute uma lógica mais complexa afetando muitas entidades de maneira atômica. Você também pode usar comandos assíncronos para cálculos mais demorados.

Existe um artigo escrito por Ali Kheyrollahi que trata dos padrões mencionados com o REST.

Veja também os modo RESTful de enviar comandos .

    
por 08.02.2017 / 13:38
fonte
0

Se não houver regras de negócios agora em torno da remoção da linha de pedidos, o método de granulação grossa ficará bem. As linhas de ordem são apenas objetos de valor. Você menciona "quando mais tarde", quando esse momento vier, altere seu modelo e possivelmente sua interface REST.

Sua camada de aplicativo pode converter seu documento em operações na coleção order.orderLines, enquanto isso é feito, as regras de negócios podem ser aplicadas.

Seu ponto 2 me confunde um pouco. O REST é supostamente sem estado, portanto você não deve estar fazendo 'acompanhamento de alterações para salvar'. Mantenha o item temporário no seu cliente, envie o resultado completo.

Sua interface REST faz parte de uma camada de aplicativo. Não precisa imitar sua semântica de domínio.

A opção de granularidade fina talvez faça mais sentido em uma situação na qual você deseja remover um item de um pedido já enviado. Você não estaria enviando documentos de pedidos inteiros neste contexto.

    
por 08.02.2017 / 03:51
fonte