Package systextil.services


package systextil.services

Pacote responsável por centralizar todas as interfaces e DTO's que são públicas para o sistema.

Com a modularização do Systêxtil, adotou-se como modelo uma arquitetura baseada em "serviços", que por sua vez são contextualizados pelo seu domínio de negócio (estoque, expedição, faturamento, vendas, clientes, ....).

No código legado, o contexto do que cada módulo pode executar por si próprio é muito aberto. Funcionalidades como a movimentação de estoques são executadas explicitamente e de diversas formas, por qualquer lugar do sistema!

Essa duplicação de código gera um custo alto para quem precisa dar manutenção no código fonte, principalmente quando é feita uma nova implementação, o que nos obriga a mapear diversos pontos de código fonte e em vários projetos, pois ainda existem implementações repetidas em formulários NXJ e em conversões mal-feitas nos projetos systextil-function, systextil-bo e systextil-batch.

Portanto, para que não continue gerando dependências entre os projetos systextil-function e systextil-bo, no que diz respeito a classes DAO, objetos e processos de negócio, toda feature de um domínio de negócio, que é invocada em outro domínio distinto, deve ser disponibilizada através da API pública que é comum a todos os módulos (a systextil-plugins-api). Logo, a API dos plugins se encarrega de exibir apenas DADOS e MÉTODOS, enquanto o módulo que implementa a interface pública se encarrega de manipular COMO executar a rotina especificada.

Os pacotes inclusos dentro de systextil.services devem ser distinguidos pelo seu respectivo módulo (systextil.services.estoque, systextil.services.faturamento, systextil.services.vendas, etc.), e as implementações devem ser concisas com o que está sendo publicado.

Caso a implementação for muito simples (em casos que o nome do método é sugestivo quanto ao que será feito), não se torna necessário documentar. Mas em casos que é necessário publicar um processo específico e utilizado em diversos lugares, é interessante que o desenvolvedor documente-o na interface localizada na própria API.

Exemplo de implementação para o módulo de estoque:

A interface é escrita dentro do projeto systextil-plugins-api:

 
 
 package systextil.services.estoque;
 
 public interface EstoqueService {
        
      public boolean transacaoAtualizaEstoque(AppConnection conn, int transacao);
      
      public void movimentarEstoque(AppConnection conn, int deposito, MovimentacaoDto dto);
 
      public TransacaoDto getTransacao(AppConnection conn, int transacao);
 }
 
 
Implementação no projeto systextil-estoque:
 
 
 package systextil.estoque.services;
 
 import systextil.estoque.movimentos.MovimentoDeEstoque;
 import systextil.estoque.dao.Transacao;
 
 public class EstoqueServiceImpl implements EstoqueService {
 
      public boolean transacaoAtualizaEstoque(AppConnection conn, int transacao) {
          return Transacao.atualizaEstoque(conn, transacao); //Classe DAO visível apenas pelo módulo de estoque
      }
      
      public void movimentarEstoque(AppConnection conn, int deposito, MovimentacaoDto movimento) {
          if("S".equals(movimento.tipoDeMovimento)){
              //Método apenas visível pelo módulo de estoque
              MovimentoDeEstoque.retirar(conn, deposito, movimento);
          } else if("E".equals(movimento.tipoDeMovimento)){
              MovimentoDeEstoque.entrar(conn, deposito, movimento);
          }
      }
 
      public TransacaoDto getTransacao(AppConnection conn, int codigoTransacao) {
          Transacao transacao = Transacao.getTransacao(conn, codigoTransacao);
          return transacao != null ? transacao.toDto() : new TransacaoDto();
      }
 }
 
 

É comum, em alguns processos, existirem chamadas de classes DAO que retornam o objeto completo lido do banco de dados. Como não serão mais publicadas as classes DAO, tomou-se como alternativa os DTO's para transferir dados para os demais módulos.

Exemplo:
 
 package systextil.services.vendas;
 
 public class TransacaoDto {
      //Declaração dos campos da tabela estq_005
 }
 
 
 Na classe EstoqueServiceImpl: 
 
 public TransacaoDto getTransacao(AppConnection conn, int codigoTransacao) {
      Transacao transacao = Transacao.getTransacao(conn, codigoTransacao);
      return transacao != null ? transacao.toDto() : new TransacaoDto();
 }
 
 
Dessa forma, um módulo externo não enxerga a classe DAO Transacao, tampouco a forma com que o método getTransacao trabalha para retornar um TransacaoDto.

Tá. Mas e agora, como eu vou poder invocar esse método?

Deve ser implementado um provedor, assim como já é feito com alguns plugins dentro deste projeto:

 
 
 package systextil.services.estoque;
 
 public class EstoqueProvedor {
      
      public static EstoqueService getService() {
          PluginClassLoader estoque = new PluginClassLoader("systextil-estoque");
          EstoqueService provedor = estoque.getProvider(EstoqueService.class);
          return provedor != null ? provedor : new EstoqueDummy();
      }
 }
 
 

Dessa forma a própria API, através do PluginClassLoader, deverá retornar uma implementação concreta de EstoqueService para o processo que irá utilizar os métodos publicados, conforme necessidade - no nosso caso, irá retornar a própria EstoqueServiceImpl.

Por padrão, deve existir somente uma implementação concreta da interface especificada, para que os processos e métodos sejam mantidos e isolados em um único lugar.

A chamada final fica da seguinte forma:
 Dentro do módulo systextil-faturamento: 
 
 
 package systextil.faturamento.calculo.FaturamentoProcess;
 
 public class FaturamentoProcess {
      //.....
      private EstoqueService estoqueService = EstoqueProvedor.getService();
      //.....
      public void validarContaContabilPedidoVendaItem(AppConnection conn, int pedido, ExercicioDto exercicio) throws TagException {
          PedidoDTO pedidoDto = vendasService.getPedido(conn, pedido);
          ClienteDTO cliente = clientesService.getCliente(conn, pedidoDto.cliente);
          NaturezaDeOperacao natCapa = NaturezaDeOperacao.get(conn, pedidoDto.natop_pv_nat_oper, pedidoDto.natop_pv_est_oper);
          
          //Aqui invoca o método implementado na classe EstoqueServiceImpl
          if (estoqueService.transacaoAtualizaContabil(conn, natCapa.codigo_transacao)) {
              verificaContasItemPedido(conn, exercicio, pedidoDto, cliente, natCapa);
          }
      }
 }
 
 

E como o PluginClassLoader vai saber exatamente que é a classe EstoqueServiceImpl que ele deve retornar no provedor?

É através de um arquivo localizado no pacote META-INF.services do módulo em questão que o PluginClassLoader identifica a implementação que deve retornar:

 Nome do arquivo: systextil.services.estoque.EstoqueService
 
 systextil.estoque.services.EstoqueServiceImpl
 
Onde o nome do arquivo corresponde a interface da API que é implementada, e o package escrito dentro do arquivo corresponde à classe, do módulo de estoque, que implementa a interface EstoqueService.

É importante frisar que o módulo de faturamento enxerga apenas a API de plugins, e não diretamente o módulo de estoque. Isso irá nos prevenir de não quebrar os demais lugares da aplicação que, por exemplo, validam se a transação gera lançamentos contábeis da mesma forma que é implementada no módulo de estoque. E, se houver algum problema relativo a tal validação, será necessário apenas alterar no módulo de estoque.