Páginas filhas
  • Integração RM x SmartLink Behavior Sharing - Visão desenvolvedor

Versões comparadas

Chave

  • Esta linha foi adicionada.
  • Esta linha foi removida.
  • A formatação mudou.

Índice
maxLevel2

01. Apresentação

Este documento técnico tem por objetivo auxiliar os desenvolvedores dos segmentos na criação de classes que "publicam" e/ou "consomem" mensagens através da plataforma Totvs SmartLink.

02. Responsabilidades

O desenvolvimento e manutenção dos publicadores e consumidores de mensagens são de responsabilidade das equipes de segmentos. Os artefatos genéricos que controlam a fila de execução das mensagens são de responsabilidade da equipe de Framework.

03. Implementando um publicador de mensagens

Para que uma classe seja um publicador de mensagens,  deve-se atender aos pré-requisitos listados abaixo:

Pré-requisitos

  1. Criar um projeto (classLibrary .NET) na solution do segmento em questão, com o seguinte padrão de nome: RM.[Segmento].XXX.SmartLink.Service.
    1. Exemplo: RM.Glb.SGDP.SmartLink.Service.dll
  2. Adicionar referência para a dll "RM.Lib.SmartLink.dll";
  3. Criar uma classe que receba em seu construtor instâncias das seguintes interfaces:

    1. "IRMSSmartLinkPublisherService": interface de serviço responsável em incluir a mensagem na fila do RM.SmartLink.Client.

      InformaçõesFoi criada na Lib uma classe de "Factory" responsável em fornecer uma instância para interface "IRMSSmartLinkPublisherService". Trata-se da classe  "RMSSmartLinkPublisherFactory"

      .

      Basta chamar seu método público "NewSmartLinkPublisher" para obter uma instância contendo essa responsabilidade.

    2. "IRMSLogger": serviço usado para incluir logs relacionados à regra de negócio em questão. 

      Informações

      Todos os logs adicionados nesse serviço serão gravados automaticamente na tabela "GTotvsLinkLog"

    3. Para publicar uma mensagem na fila do RM.SmartLink.Client, basta chamar o método "AddMessage" da instância de interface "IRMSSmartLinkPublisherService". Os seguinte dados devem ser enviados através de uma classe de parâmetros do tipo "SmartLinkPublisherAddMessageParams":

      PropriedadeDescrição
      CommandNome do comando a ser incluído na fila do SmartLink. Ex: SGDPUpdateTenantMetada
      CorrelatedIdPode ser ser enviado nessa propriedade um identificador (guid) que correlacionam mensagens de envio e resposta.
      Data

      Dados da mensagem contendo informações relacionadas ao negócio. Pode ser em qualquer formato (json, xml, etc)  desde que o consumidor consiga interpretá-lo.

      RouterMessageRota de envio da mensagem. Será concatenada ao endpoint do serviço do Totvs SmartLink.Server. ex: /api/v1/link/send/SGDPMaskResponse/SGDP


  4. Diagrama de classes contendo um publicador de mensagem de exemplo:

...

Bloco de código
languagec#
firstline1
linenumberstrue
using Newtonsoft.Json.Linq;
using RM.Glb.SGDP.SmartLink.Service.Domain.Interfaces;
using RM.Lib.DataGlb.SGDP.SmartLink.Service.Domain.Interfaces;
using RM.Lib.Log;
using RM.Lib.SmartLink.Domain.DataModel;
using RM.Lib.SmartLink.Domain.Interfaces;
using RM.Lib.SmartLink.Domain.Publisher;
using System;
using System.Collections.Generic;
using System.Linq;

namespace RM.Glb.SGDP.SmartLink.Service
{
  public class GlbSGDPPublisherMessageService : IGlbSGDPPublisherMessageService
  {
    private readonly IGlbSGDPResolverService _SGDPResolverServicesgdpResolverService;
    private readonly IRMSSmartLinkPublisherService _SmartLinkPublisherServicesmartLinkPublisherService;
    private readonly IRMSLogger _LogServicelogService;

    private const string ctSGDPMaskResponseEndPointctSGDPUpdateTenantMetadataEndPoint = "/api/v1/link/send/SGDPMaskResponseSGDPUpdateTenantMetadata/SGDP";
    private const string ctSGDPDataResponseEndPoint = "/api/v1/link/send/SGDPDataResponse/SGDP";

    private const string ctSGDPUpdateTenantMetadataEndPointctSGDPUpdateTenantMetadata = "/api/v1/link/send/SGDPUpdateTenantMetadata/SGDP";


    private const string ctSGDPUpdateTenantMetadatactSGDPDataCommand = "SGDPUpdateTenantMetadataSGDPDataCommand";
    private const string ctSGDPDataCommandctSGDPResponseDataCommand = "SGDPDataCommandSGDPResponseDataCommand";

    private const string ctSGDPMaskCommandctSGDPMaskResponseEndPoint = "SGDPMaskCommand/api/v1/link/send/SGDPMaskResponse/SGDP";
    private const string ctSGDPResponseDataCommandctSGDPMaskCommand = "SGDPResponseDataCommandSGDPMaskCommand";
    private const string ctSGDPResponseMaskCommand = "SGDPResponseMaskCommand";

    private const string ctSmartLinkSetupCommandctSGDPLogResponseEndPoint = "Setup/api/v1/link/send/SGDPLogsResponse/SGDP";
    private const string ctSmartLinkUnSetupCommandctSGDPLogsCommand = "UnSetupSGDPLogsCommand";


    publicprivate GlbSGDPPublisherMessageService(IRMSSmartLinkPublisherService smartLinkPublisherService, 
  const string ctSGDPResponseLogCommand = "SGDPResponseLogsCommand";

    IGlbSGDPResolverServiceprivate sgdpResolverService,const 
string ctSmartLinkSetupCommand = "setup";
    private const string ctSmartLinkUnSetupCommand = "unSetup";


    public GlbSGDPPublisherMessageService(IRMSSmartLinkPublisherService smartLinkPublisherService,
      IGlbSGDPResolverService sgdpResolverService,
      IRMSLogger logService)
    {
      _SmartLinkPublisherServicesmartLinkPublisherService = smartLinkPublisherService;
      _SGDPResolverServicesgdpResolverService = sgdpResolverService;
      _LogServicelogService = logService;
    }

    /// <summary>
    /// Adiciona uma mensagem de reposta para o comando "SGDPResponseDataCommand".
    /// </summary>
    /// <param name="parms"></param>
    public void AddResponseDataCommand(AddResponseDataCommandParms parms)
    {
      SmartLinkPublisherAddMessageParams parPublisher = new SmartLinkPublisherAddMessageParams();
      parPublisher.{
        Command = ctSGDPResponseDataCommand;,
        parPublisher.CorrelatedId = parms.CorrelatedId
      };
      ;
      parPublisher.Data = parms.Message;
      parPublisher.RouterMessage = ctSGDPDataResponseEndPoint;
      _LogServicelogService.NotifyLogInfo(Properties.Resources.sconTotvsAppSGDPResponseDataCommandGravadoNaFila);
      _SmartLinkPublisherServicesmartLinkPublisherService.AddMessage(parPublisher);
    }

    /// <summary>
    /// Adiciona uma mensagem de reposta para o comando "SGDPResponseMaskCommand".
    /// </summary>
    /// <param name="parms"></param>
    public void AddResponseMaskCommand(AddResponseMaskCommandParms parms)
    {
      SmartLinkPublisherAddMessageParams parPublisher = new SmartLinkPublisherAddMessageParams();

      {
        parPublisher.Command = ctSGDPResponseMaskCommand;,
      parPublisher.  CorrelatedId = parms.CorrelatedId;,
        parPublisher.Data = parms.Message;,
        parPublisher.RouterMessage = ctSGDPMaskResponseEndPoint
      };
      _LogServicelogService.NotifyLogInfo(Properties.Resources.sconTotvsAppSGDPResponseMaskCommandGravadoNaFila);
      _SmartLinkPublisherServicesmartLinkPublisherService.AddMessage(parPublisher);
    }

    /// <summary>
    /// Adiciona uma mensagem parade envioreposta depara umo comando de sgldpdateapplicationmetada"SGDPResponseLogCommand".
    /// </summary>
    /// <param name="parms">Parms</param>
    public void AddMessageUpdateTenantMetadataAddResponseLogCommand(AddMessageUpdateTenantMetadataParmsAddResponseLogCommandParms parms)
    {
      if (CanAddTenantMetadataMessage())SmartLinkPublisherAddMessageParams parPublisher = new SmartLinkPublisherAddMessageParams
      {
        try
Command        {= ctSGDPResponseLogCommand,
          List<string> jsonsCorrelatedId = GetUpdateTenandMetadata(parms.TenantId);CorrelatedId,
        Data  foreach (string json in jsons)= parms.Message,
        RouterMessage = {ctSGDPLogResponseEndPoint
      };
      SmartLinkPublisherAddMessageParams smartLinkParms = new SmartLinkPublisherAddMessageParams(_logService.NotifyLogInfo(Properties.Resources.sconTotvsAppSGDPResponseLogCommandGravadoNaFila);
      _smartLinkPublisherService.AddMessage(parPublisher);
    }

   smartLinkParms.Command = ctSGDPUpdateTenantMetadata; /// <summary>
    /// Adiciona uma mensagem para envio de um smartLinkParms.CorrelatedIdcomando = "";de sgldpdateapplicationmetada.
    /// </summary>
    public void  smartLinkParms.Data = json;AddMessageUpdateTenantMetadata(AddMessageUpdateTenantMetadataParms parms)
    {
      if  smartLinkParms.RouterMessage = ctSGDPUpdateTenantMetadataEndPoint;
(CanAddTenantMetadataMessage())
      {
        try
        _LogService.NotifyLogInfo(Properties.Resources.sconTotvsAppSGDPTenantMetadaGravadoNaFila);{
          List<string> jsons = _SmartLinkPublisherService.AddMessage(smartLinkParmsGetUpdateTenandMetadata(parms.TenantId);
          }
foreach (string json in jsons)
    }
      {
  catch (Exception ex)
         SmartLinkPublisherAddMessageParams  {
smartLinkParms = new SmartLinkPublisherAddMessageParams();
            _LogService.NotifyLogWarning(ex, Properties.Resources.sconTotvsAppErroAoEnviarSGDPUpdateTenantMetadata)smartLinkParms.Command = ctSGDPUpdateTenantMetadata;
        }
    smartLinkParms.CorrelatedId = }"";
    }

    private bool CanAddTenantMetadataMessage()
  smartLinkParms.Data = {json;
      //Se existir  alguma mensagem de sgdpsmartLinkParms.RouterMessage para= serctSGDPUpdateTenantMetadataEndPoint;
 processada, então não envia atualizaçaõ de medatdos pois esse processo é caro.
 _logService.NotifyLogInfo(Properties.Resources.sconTotvsAppSGDPTenantMetadaGravadoNaFila);
        var  pendingMessages = _SmartLinkPublisherServicesmartLinkPublisherService.GetPendingMessagesAddMessage(smartLinkParms);
      List<SmartLinkMessageDataModel>  messageUpstream = pendingMessages?.Messages?.Where( }
        y}
 => y.TypeEvent == ctSGDPUpdateTenantMetadata ||
   catch (Exception ex)
        y.TypeEvent == ctSGDPDataCommand ||
{
          _logService.NotifyLogWarning(ex, Properties.Resources.sconTotvsAppErroAoEnviarSGDPUpdateTenantMetadata);
     y.TypeEvent == ctSGDPMaskCommand ||}
      }
    }

   y.TypeEvent ==private ctSGDPResponseDataCommand ||bool CanAddTenantMetadataMessage()
    {
      //Se existir alguma y.TypeEvent == ctSGDPResponseMaskCommand).ToList();
      if (messageUpstream?.Count > 0)//Se existir alguma não inclua na fila..mensagem de sgdp para ser processada, então não envia atualizaçaõ de medatdos pois esse processo é caro.
      var pendingMessages return false= _smartLinkPublisherService.GetPendingMessages();
      return true;
List<SmartLinkMessageDataModel> messageUpstream = pendingMessages?.Messages?.Where(
      }

  y => private List<string> GetUpdateTenandMetadata(string tenantId)
y.TypeEvent == ctSGDPUpdateTenantMetadata ||
      {
      return _SGDPResolverService.GetSGDPTenantMetadata(y.TypeEvent == ctSGDPDataCommand ||
        new   MetadataTenantParms() { TenantIdy.TypeEvent == tenantIdctSGDPMaskCommand }||
        )?.JsonsResult;
    }

 y.TypeEvent == ctSGDPResponseDataCommand public||
 void AddMessageUnSetup()
    {
      var pendingMessagesy.TypeEvent == _SmartLinkPublisherServicectSGDPResponseMaskCommand).GetPendingMessagesToList();
      List<SmartLinkMessageDataModel> messagesSGDP = 
        pendingMessages?.Messages?.Where(y => y.TypeEvent.ToUpper() == ctSGDPUpdateTenantMetadata.ToUpper() &&if (messageUpstream?.Count > 0)//Se existir alguma não inclua na fila...
        return false;
      return true;
    }

    private List<string> GetUpdateTenandMetadata(string tenantId)
    {
        return _sgdpResolverService.GetSGDPTenantMetadata(
       y.TypeEvent.ToUpper new MetadataTenantParms() { =TenantId = ctSGDPDataCommand.ToUpper() &&
tenantId }
        )?.JsonsResult;
    }

    public void AddMessageUnSetup()
    {
      var pendingMessages = _smartLinkPublisherService.GetPendingMessages();
      List<SmartLinkMessageDataModel> messagesSGDP =
        ypendingMessages?.TypeEvent.Messages?.Where(y => y.TypeEvent.ToUpper() == ctSGDPMaskCommandctSGDPUpdateTenantMetadata.ToUpper() &&
                                              y.TypeEvent.ToUpper() == ctSGDPResponseDataCommandctSGDPDataCommand.ToUpper() &&
                                              y.TypeEvent.ToUpper() == ctSGDPResponseMaskCommandctSGDPMaskCommand.ToUpper()).ToList(); &&
      foreach (SmartLinkMessageDataModel model in messagesSGDP)
        _SmartLinkPublisherService.RemoveMessageById(model.Id);

      SmartLinkPublisherAddMessageParams parms = new SmartLinkPublisherAddMessageParams();
      parms.Command = ctSmartLinkUnSetupCommand;
      parms.CorrelatedId = "";
      parms.Datay.TypeEvent.ToUpper() == _SGDPResolverServicectSGDPLogsCommand.GetSGDPSetupToUpper().JsonResult; &&
      _LogService.NotifyLogInfo(Properties.Resources.sconTotvsAppUnSetupGravadoNaFila);
      _SmartLinkPublisherService.AddMessage(parms);
    }

    public void AddMessageSetup()
    {
      SmartLinkPublisherAddMessageParams parms = new SmartLinkPublisherAddMessageParams();
           parms.Command = ctSmartLinkSetupCommand;
y.TypeEvent.ToUpper() == ctSGDPResponseDataCommand.ToUpper() &&
        parms.CorrelatedId = "";
      parms.Data = _SGDPResolverService.GetSGDPSetup().JsonResult;
      _LogService.NotifyLogInfo(Properties.Resources.sconTotvsAppSetupGravadoNaFila);
      _SmartLinkPublisherService.AddMessage(parms                y.TypeEvent.ToUpper() == ctSGDPResponseMaskCommand.ToUpper()).ToList();

    }
  if (messagesSGDP != null)
      }
}


Para que uma classe seja um consumidor de mensagens no SmartLink, deve-se atender aos pré-requisitos listados abaixo:

{
        foreach (SmartLinkMessageDataModel model in messagesSGDP)
        {
          _smartLinkPublisherService.RemoveMessageById(model.Id);
        }
      }

      SmartLinkPublisherAddMessageParams parms = new SmartLinkPublisherAddMessageParams
      {
        Command = ctSmartLinkUnSetupCommand,
        CorrelatedId = "",
        Data = _sgdpResolverService.GetSGDPSetup().JsonResult
      };
      _logService.NotifyLogInfo(Properties.Resources.sconTotvsAppUnSetupGravadoNaFila);
      _smartLinkPublisherService.AddMessage(parms);
    }

    public void AddMessageSetup()
    {
      SmartLinkPublisherAddMessageParams parms = new SmartLinkPublisherAddMessageParams
      {
        Command = ctSmartLinkSetupCommand,
        CorrelatedId = "",
        Data = _sgdpResolverService.GetSGDPSetup().JsonResult
      };
      _logService.NotifyLogInfo(Properties.Resources.sconTotvsAppSetupGravadoNaFila);
      _smartLinkPublisherService.AddMessage(parms);
    }
  }
}


04. Implementando um consumidor de mensagens

Para que uma classe seja um consumidor de mensagens no SmartLink, deve-se atender aos pré-requisitos listados abaixo:

Pré-requisitos

  1. Criar um projeto (classLibrary .NET) na solution do segmento em questão, com o seguinte padrão de nome: RM.[Segmento].XXX.SmartLink.Service.
    1. Exemplo: RM.Glb.SGDP.SmartLink.Service.dll
  2. Adicionar referência para a dll "RM.Lib.SmartLink.dll";
  3. Criar uma classe herdando da ancestral "RMSSmartLinkConsumerMessageBase".

  4. A classe "RMSSmartLinkConsumerMessageBase" herda da classe RMSObject da Lib. Consequentemente, as classes de "consumers" poderão chamar os métodos "CreateFacade" e "CreateModule" dentro de suas estruturas.
  5. Carimbar a classe com o atributo "RMSSmartLinkConsumerMessageAttr". Nesse atributo, devem ser informados os dados abaixo:

    PropriedadeDescrição
    Cod.SistemaIdentificador da aplicação
    ComandoNome do Comando da mensagem
  6. Para realização de tratamento de exceções no caso de ocorrer erro no processamento da mensagem
    Quando for considerado um erro e um nova tentativa de processamento deve ser realizada: 
    A classe RMSSmartLinkConsumerMessageException ou outra, pode ser utilizada.
    Neste caso a mensagem será reenfileirada e novas tentativas de processamento serão realizadas.

    Sendo considerado que a mensagem deve ser descartada, ou seja, esta mensagem não deve ser processada neste momento ou apenas sob intervenção manual:
    A classe RMSSmartLinkConsumerFatalException, deverá ser utilizada e informado o motivo.
    Neste caso a mensagem será colocada em um fila de espera (DLQ) e somente será reprocessada por uma intervenção manual ou será descartada.
  7. Criar um projeto (classLibrary .NET) na solution do segmento em questão, com o seguinte padrão de nome: RM.[Segmento].XXX.SmartLink.Service.
    1. Exemplo: RM.Glb.SGDP.SmartLink.Service.dll
  8. Adicionar referência para a dll "RM.Lib.SmartLink.dll";
  9. Criar uma classe herdando da ancestral "RMSSmartLinkConsumerMessageBase".

  10. A classe "RMSSmartLinkConsumerMessageBase" herda da classe RMSObject da Lib. Consequentemente, as classes de "consumers" poderão chamar os métodos "CreateFacade" e "CreateModule" dentro de suas estruturas.
  11. Carimbar a classe com o atributo "RMSSmartLinkConsumerMessageAttr". Nesse atributo, devem ser informados os dados abaixo:

    PropriedadeDescriçãoCod.SistemaIdentificador da aplicaçãoComandoNome do Comando da mensagem

  12. Código fonte de exemplo (extraído da classe "GlbSGDPConsumerDataCommandMessage" localizada na solution de Globais, projeto "RM.Glb.SGDP.SmartLink.Service":

    Bloco de código
    languagec#
    firstline1
    linenumberstrue
    using RM.Glb.SGDP.SmartLink.Service.Domain.Interfaces;
    using RM.Lib;
    using RM.Lib.SmartLink.Domain;
    using RM.Lib.SmartLink.Domain.Consumer;
    using RM.Lib.SmartLink.Domain.Interfaces;
    using RM.Lib.SmartLink.Domain.Publisher;
    using System;
    using System.Runtime.Serialization;
    
    namespace RM.Glb.SGDP.SmartLink.Service.Domain
    {
      /// <summary>
      /// Regras de implementação do mecanismo de processamento da mensagem SGDDataCommand do SGDP
      /// </summary>
      [RMSSmartLinkConsumerMessageAttr(CodSistema.Glb, "SGDPDataCommand")]
      public class GlbSGDPConsumerDataCommandMessage : RMSSmartLinkConsumerMessageBase
      {
        protected override ConsumerMessageExecuteResult DoExecute(string message)
        {
          logService.NotifyLogInfo(Properties.Resources.sconTotvsAppInicioExecucaoDoConsumerSGDPDataCommandConsumer);
          ConsumerMessageExecuteResult result = new ConsumerMessageExecuteResult();
          IGlbSGDPResolverService resolver = GlbSGDPResolveFactory.NewSGDPResolve(this.DBS, this.logService);
          IRMSSmartLinkPublisherService smartLinkPublisherService = RMSSmartLinkPublisherFactory.NewSmartLinkPublisher(this.DBS, this.logService);
          IGlbSGDPPublisherMessageService publisher = new GlbSGDPPublisherMessageService(smartLinkPublisherServicethis.SmartLinkPublisherService, resolver, logService);
    
          ExecuteDataCommandParms par = new ExecuteDataCommandParms();
          par.Message = message;
          try
          {
            var execDataResult = resolver.ExecuteSGDPDataCommand(par);
            logService.NotifyLogInfo(Properties.Resources.sconTotvsAppJSonSGDPDataResponseCommandForamGerados, "Quantidade JSons Gerados" 
              Properties.Resources.sconTotvsAppQuantidadeDeJSonsGerados, execDataResult?.JsonResults?.Count);
            result.CorrelatedID = execDataResult.RequestId;
            if (execDataResult?.JsonResults != null)
            {
              foreach (var jsonResult in execDataResult.JsonResults)
              {
                AddResponseDataCommandParms parResponse = new AddResponseDataCommandParms();
                parResponse.Message = jsonResult;
                parResponse.CorrelatedId = execDataResult.RequestId;
                publisher.AddResponseDataCommand(parResponse);
              }
            }
          }
          catch(Exception ex)
          {
            logService.NotifyLogError(new GlbSGDPConsumerDataCommandMessageException(
              Properties.Resources.sconTotvsAppErroAoProcessarSGDDataCommand, ex));
          }
          logService.NotifyLogInfo(Properties.Resources.sconTotvsAppFimExecucaoDoConsumerSGDPDataCommandConsumer);
          return result;
        }
      }
    
      [Serializable]
      public class GlbSGDPConsumerDataCommandMessageException : RMSApplicationException
      {
        public GlbSGDPConsumerDataCommandMessageException() : base()
        {
        }
    
        public GlbSGDPConsumerDataCommandMessageException(string message) : base(message)
        {
        }
    
        public GlbSGDPConsumerDataCommandMessageException(string message, Exception ex) : base(message(message, ex, string.Empty)
        {
        }
    
        public GlbSGDPConsumerDataCommandMessageException(stringSerializationInfo messageinfo, ExceptionStreamingContext excontext)
      : base(messageinfo, ex, string.Emptycontext)
        {
        }
    
        public GlbSGDPConsumerDataCommandMessageException(SerializationInfo info, StreamingContext context)
      : base(info, context)
        {
        }
      }
    }
    
    
    

Image Removed

...

  1. 
      }
    }

05. Diagrama de classes

Image Added


06. DER


Image Added


Tabela
GTOTVSLINKMESSAGE

Tabela utilizada para armazenar todos as mensagens geradas e enviadas para execução pelo SmartLink.

Sempre que uma mensagem é executa com sucesso, a linha referente a mensagem é excluída

dessa tabela , deixando essa tabela sempre leve. Esse procedimento melhora a performance do processo,

visto que vários "updates" são realizados nessa tabela.Quando menor o volume de uma tabela, mais rápido

será a operação do update.

GTOTVSLINKMESSAGEEXEC

Tabela contendo todos os dados de execução de uma determinada mensagem.


GTOTVSLINKLOGTabela contendo todos os logs de execução de uma mensagem.