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 classeDessa forma, um módulo externo não enxerga a classe DAOEstoqueServiceImpl
:public TransacaoDto getTransacao(AppConnection conn, int codigoTransacao) { Transacao transacao = Transacao.getTransacao(conn, codigoTransacao); return transacao != null ? transacao.toDto() : new TransacaoDto(); }
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ódulosystextil-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.EstoqueServiceImplOnde 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.