DEV Community

Josimar Junior
Josimar Junior

Posted on

Um microblog usando Protheus - Rest Server, parte 4, classe para rest + modelos MVC

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, 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.

Por que esta implementação?

A implementação demonstrada a seguir não muda muito da demonstrada na segunda parte desta série, 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 nesta documentação 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.

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:

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()
Enter fullscreen mode Exit fullscreen mode

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.

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")
Enter fullscreen mode Exit fullscreen mode
  • 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 POSTe 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.

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()
Enter fullscreen mode Exit fullscreen mode

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.

lDelete := ZT0->(DbSeek(xFilial("ZT0")+self:perfilId))
oModel:SetOperation(MODEL_OPERATION_DELETE)
lProcessed := oModel:Activate()
lProcessed := lProcessed .And. oModel:VldData() .And. oModel:CommitData()
Enter fullscreen mode Exit fullscreen mode

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.
A próxima etapa será implementar recursos como paginação e ordem na recuperação de lista dos itens GET geral.

Top comments (0)