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