Um microblog usando Protheus - Rest Server, parte 4, classe para rest + modelos MVC
Tags: advpl, mvc, rest, api
Published: 2020-12-07 00:41:55

# Introdução
Nesta parte da série de publicações será abordado como construir o conjunto de operações para incluir, buscar, atualizar e excluir um item utilizando um modelo de dados MVC para as operações que envolvem o banco de dados.
Para essa demonstração o modelo de dados utilizado é o construído na [parte anterior](https://dev.to/josimar_jr_/um-microblog-usando-protheus-rest-server-parte-3-1j2k), onde foi demonstrado como fazer a publicação de um modelo MVC no Rest e utilizada as URLs padrões `/fwmodel/{modelo}/{pk}` para realizar as requisições rest. Portanto, em caso de dúvida da implementação do modelo usando nas operações a seguir consulte o código advpl no [link](https://github.com/josimar-jr/microblog-protheus/blob/6428ea4299dad6f34cd1723b9f712651f02064b4/src/perfis/ZMBA010.prw#L27).

# Por que esta implementação?
A implementação demonstrada a seguir não muda muito da demonstrada na [segunda parte desta série](https://dev.to/josimar_jr_/um-microblog-usando-protheus-rest-server-parte-2-1bch), contudo a diferença que existe já proporciona um isolamento de responsabilidades melhor e para algumas situações específicas é uma implementação boa o bastante.
O principal ganho desta implementação comparado com a demonstrada na segunda parte está no fato de que as validações e a gravação dos registros quando inclusão, alteração e exclusão não ficam na mesma função da api/requisição e sim em uma camada interna que pode evoluir de forma independente. Essa independência é relevante para a evolução dos serviços e contextos de negócios envolvidos na api.
Existem várias abordagens com o propósito de separar essas camadas de responsabilidade, mas este não será o momento de explorar qualquer um destes modelos com Advpl e Protheus, isso será feito mais adiante na série. Para as pessoas que possuirem interesse em pesquisar neste momento, alguns dos temas e termos eles são N Layers, Hexagonal Architecture, Clean Architecture, Actors Model e a Orientação a Objetos no design de aplicações, estes são os que lembro no momento.

# O efeito do modelo de dados no Protheus!
Um dos principais recursos que o MVC Advpl oferece é a possibilidade de utilizar um determinado contexto de negócio em outros processos ou entidades, seja este primeiro modelo uma entidade ou um processo. Com isso é possível por exemplo na operação de criação de um registro na tabela A, também inserir um registro na tabela B ou qualquer outra entidade que estenda informações do registro na tabela A e tudo isso sem ferir regras de integridade e validações de negócio envolvidos em cada uma dessas tabelas.
Isso é conseguido com o comando `FwLoadModel("arquivo")` que recupera a definição do modelo de dados dentro da `static function ModelDef()` e depois disso permite indicar a operação e os dados para criar, recuperar, atualizar ou excluir os registros.
Esta possibilidade de reuso das regras é o que inibe a replicação de diversas validações, inclusive eventuais regras que não estão no dicionário de campos/SX3, e como vantagem adicional a gravação dos dados no modelo pode ser específica deste modelo e isso também será reutilizado.
O único aspecto que acaba não sendo atendido com o uso do modelo MVC combinado com a classe Rest é a listagem dos registros. Isso por que quando utilizado através de tela no Smartclient dentro do Protheus a responsabilidade de listar os itens é do browse e por isso não há implementação no MVC que exiba uma lista por padrão. Em função disso os métodos apresentados a seguir são para operações em um item, um único registro.
Para as pessoas que não entenderam e se perguntaram como a publicação do modelo padrão lida com isso e se perguntaram "Como a `/fwmodel/` faz a listagem?" leia o modo avançado da [parte anterior](https://dev.to/josimar_jr_/um-microblog-usando-protheus-rest-server-parte-3-1j2k) nesta [documentação](https://tdn.totvs.com/display/public/PROT/FWRestModel) e veja a implementação base da classe `FwRestModelObject`.

# Construindo a v2 para api de Perfis
Será apresentada a versão 2 da api para Perfis do microblog e os aspectos que mudaram da v1 para a v2 que serão destacados.
Os comandos relacionados à classe rest Advpl já mencionados não serão explicados novamente, caso tenha dúvida nestes pontos pode consulta a [segunda parte da série](https://dev.to/josimar_jr_/um-microblog-usando-protheus-rest-server-parte-2-1bch).

## POST /microblog/v2/perfis/
A operação de inclusão na `v1` utilizava `Reclock` e `MsUnlock` e só validava a existência dos conteúdos que vieram no `body` da requisição e esse tipo de validação é insuficiente para 99% dos casos.
Isso se tornou os seguintes comandos:
```c
oModel:SetOperation(MODEL_OPERATION_INSERT)

lProcessed := oModel:Activate()
oZT0Header := oModel:GetModel("ZT0_FIELDS")

lProcessed := lProcessed .And. oZT0Header:SetValue("ZT0_EMAIL" , jBody["email"])
lProcessed := lProcessed .And. oZT0Header:SetValue("ZT0_USRID" , jBody["user_id"])
lProcessed := lProcessed .And. oZT0Header:SetValue("ZT0_NOME"  , jBody["name"])

lProcessed := lProcessed .And. oModel:VldData() .And. oModel:CommitData()
```
O número de linhas aumentou e o código pode ter ficado mais complicado, mas os benefícios já mencionados são garantidos com estas linhas.
- `:SetValue("ZT0_EMAIL" , jBody["email"])` => quando adicionada uma validação para o campo de email ela passará a ser aplicada aqui.
- `oModel:VldData() .And. oModel:CommitData()` => quando alterada a validação geral do modelo ou como a gravação acontece elas também estarão disponíveis aqui.

## GET /microblog/v2/perfis/{perfilId}
A recuperação de um registro usando o modelo MVC acontece da seguinte forma.
```c
lProcessed := (!(Alltrim(self:perfilId) == "") .And. ZT0->(DbSeek(xFilial("ZT0")+self:perfilId)))
oModel:SetOperation(MODEL_OPERATION_VIEW)

lProcessed := oModel:Activate()

if lProcessed
    oZT0Header := oModel:GetModel("ZT0_FIELDS")

    jResponse["email"]   := oZT0Header:GetValue("ZT0_EMAIL")
```
- `oModel:Activate()` => comando que baseado no registro posicionado na tabela realiza a carga dos dados.
- `:GetValue("ZT0_EMAIL")` => como a informação do campo é recuperada e redirecionada para a resposta.
No caso da api para perfis quase não há vantagem entre o `DbSeek` e a leitura direto do registro na tabela (ZT0->ZT0_EMAIL) para o modelo, pois é um único registro. Isso muda de situação quando o cenário da api é por exemplo para publicações e comentários de uma publicação, como é exigido carregar elementos associados (a lista de comentários de uma publicação ou à qual publicação um comentário pertence), simplesmente posicionar nas tabelas não resolve, o uso do modelo ganha vantagem pois essa lógica e o eventual relacionamento usado estão na definição do modelo e só são usados pelo serviço da api com o modelo MVC instanciado.

## PUT /microblog/v2/perfis/{perfilId}
A alteração e atualização dos campos acontece já utilizando os comandos mencionados nas operações de `POST`e `GET` e não há nenhum comando ou método novo do modelo MVC.
O que muda é a operação definida antes do `Activate` do modelo.
Então a seguir as principais linhas em Advpl na classe do Rest para suportar a operação `PUT`.
```c
lProcessed := (!(Alltrim(self:perfilId) == "") .And. ZT0->(DbSeek(xFilial("ZT0")+self:perfilId)))
...
oModel:SetOperation(MODEL_OPERATION_UPDATE)
lProcessed := oModel:Activate()
lProcessed := lProcessed .And. oZT0Header:SetValue("ZT0_NOME"  , jBody["name"])
lProcessed := lProcessed .And. oModel:VldData() .And. oModel:CommitData()
```

## DELETE /microblog/v2/perfis/{perfilId}
Assim como na operação `PUT` nada novo é adicionado nos comandos do modelo, somente o tipo de operação indicada no método `Activate`. Os principais comandos em Advpl que suportam o `DELETE` na classe Rest estão a seguir.
```c
lDelete := ZT0->(DbSeek(xFilial("ZT0")+self:perfilId))
oModel:SetOperation(MODEL_OPERATION_DELETE)
lProcessed := oModel:Activate()
lProcessed := lProcessed .And. oModel:VldData() .And. oModel:CommitData()
```

# Conclusão
O código mudou o resultado api e classe? não. Os formatos de entrada e resposta continuaram os mesmos.
Os ganhos de usar o modelo é não repetir validações e principalmente não fazer acesso direto às tabelas para recuperar conteúdo. É uma versão ideal para um serviço rest? Bem, não exatamente. Existem tópicos a serem melhorados e o principal é ter uma abstração receba os dados da api e só se comunique com o modelo e a resposta dessa abstração vai depois para o método que atendeu à requisição, algo a ser abordado numa das publicações futuras.
É possível conferir o código feito exclusivamente para publicação neste [link](https://github.com/josimar-jr/microblog-protheus/pull/2).
A próxima etapa será implementar recursos como paginação e ordem na recuperação de lista dos itens `GET` geral.