Separando Projetos Java

12

Eu tenho um grande projeto java e usamos o maven para o nosso ciclo de construção. Esse projeto é amplamente utilizado - em outros projetos, em várias aplicações, algumas das quais estão contidas e outras em outros lugares ... Para ser honesto, é um pouco confuso (vários bits são adicionados em momentos diferentes para detalhes específicos). fins) e gostaria de limpá-lo um pouco. Além disso, ele não foi totalmente testado (muitos bits foram adicionados sem testes adequados de unidade e integração), e existem alguns testes que demoram muito tempo para serem executados ou que na verdade não passam ... (uh-oh) - então os testes são desativados no ciclo de construção do maven (novamente, uh-oh).

Estou pensando em separar esse grande projeto em projetos específicos menores, de modo que o subprojeto 'final' (ou vários subprojetos) selecionasse os vários subprojetos de que precisaria.

Meu pensamento é o seguinte:

  • se eu separar o grande projeto em vários subprojetos, isso deixa claro qual é a responsabilidade de cada projeto.
  • separando-o em subprojetos, posso limpar os testes de cada subprojeto individualmente e ativar os testes para esse subprojeto no ciclo de criação do maven.

Estou um pouco preocupado com o impacto que isso pode ter no tempo de compilação.

  • A imposição de uma estrutura no projeto grande (ou seja, em subprojetos menores) atrasaria o compilador?

Além disso, tenho uma ligeira preocupação com o impacto que isso pode ter no tempo de edição nos IDEs (usamos principalmente o Intellij). O Intellij parece construir cada projeto alternadamente através da árvore de dependência - ou seja, se C depende de B depende de A e eu altero A, ele não tentará construir B a menos que A seja compilado, e assim por diante. Provavelmente isso é vantajoso, mas descobri que, por exemplo, se eu mudar uma interface em A que é amplamente usada em B e C, leva algum tempo para corrigir todos os erros dessa alteração ...

Outra questão é como usar as classes de fábrica. Alguns aspectos do projeto dependem de frascos externos. Ocasionalmente (felizmente, não com frequência), elas são atualizadas e precisamos migrar. Nós tendemos a lidar com isso usando uma classe Factory que aponta para as versões corretas do código externo (portanto, não precisamos alterar todas as implementações em toda a base de código).

No momento, tudo isso está no grande projeto, mas estou pensando que, ao mudar para subprojetos, eu poderia desenvolver um novo projeto para a implementação de um novo código externo, garantir que o subprojeto esteja totalmente funcional e testado, e depois alterne as dependências / classe de fábrica no projeto do usuário. No entanto, isso se torna mais complicado pelo uso extensivo de interfaces em todo o grande projeto. Por exemplo

  • subprojeto A - contém interfaces
  • subprojeto B - depende de A para interfaces e jar externo antigo
  • subprojeto C - depende de B (e, portanto, de A e jar externo antigo) e contém uma classe Factory que usa implementações de interfaces de B

Se eu precisar alterar o jar externo de B, posso:

  • criar subprojeto B_ii - novamente depende de A, e agora o novo jar externo
  • uma vez totalmente funcional, posso adicionar a dependência de C a B_ii e alterar a classe Factory para usar as novas implementações de interfaces.
  • quando tudo estiver funcionando, posso remover a dependência de C do original B e, se desejar, remover o subprojeto B.

  • Essa é uma maneira sensata de fazer isso?

Então, em geral, minhas perguntas são:

  • Alguém tem alguma experiência em dividir grandes projetos? Existem dicas / truques que você gostaria de compartilhar?
  • Que impacto isso teve no seu desenvolvimento e nos tempos de construção?
  • Que conselho você poderia oferecer para estruturar essa ruptura de um projeto?
amaidment
fonte
Observe que você pode criar um projeto pai com A, B e C marcados separadamente um ao lado do outro. Isso permite, por exemplo, ao IntelliJ ter todos os três na memória ao mesmo tempo e fazer refatorações através dos limites do módulo.
Thorbjørn Ravn Andersen

Respostas:

10

Vou fazer um primeiro corte rápido nisso (ótimo Q BTW!):

A imposição de uma estrutura no projeto grande (ou seja, em subprojetos menores) atrasaria o compilador?

Não o suficiente para importar, a sobrecarga está realmente nas invocações do Maven.

Além disso, tenho uma ligeira preocupação com o impacto que isso pode ter no tempo de edição nos IDEs (usamos principalmente o Intellij). O Intellij parece construir cada projeto alternadamente através da árvore de dependência - ou seja, se C depende de B depende de A e eu altero A, ele não tentará construir B a menos que A seja compilado, e assim por diante. Provavelmente isso é vantajoso, mas descobri que, por exemplo, se eu mudar uma interface em A que é amplamente usada em B e C, leva algum tempo para corrigir todos os erros dessa alteração ...

Diferentes IDEs têm seus diferentes pontos fortes em relação às ligações do Maven e ao gerenciamento de dependências. O estado atual do jogo parece ser que ele funciona principalmente nos mais recentes Eclipse, Netbeans e IntelliJ - mas você precisará ensinar aos desenvolvedores o golpe duplo de emergência "Atualizar arquivos de origem do disco e reconstruir todos os projetos relacionados".

Acho que tenho que fazer isso cada vez menos atualmente. Ter uma unidade SSD faz uma enorme diferença aqui BTW.

recorte parágrafos de classes de fábrica

O gerenciamento de dependências é incrivelmente importante, independentemente de qual tecnologia (Maven / Ivy / qualquer que seja) use para ajudá-lo a implementá-lo.

Eu começaria tirando os extensos relatórios do plug-in de dependência do Maven e analisando o que você tem. De um modo geral, você define a dependência no gerenciamento de dependências do POM o mais alto possível na cadeia alimentar, mas não maior. Portanto, se dois de seus submódulos usarem uma dependência externa, leve-a para o POM pai e assim por diante.

A atualização de JARs externos sempre deve ser feita como um miniprojeto adequado. Avalie por que você está atualizando, altere o código-fonte para tirar proveito de quaisquer novos recursos / correções de bugs, etc. Basta pressionar a versão sem essa análise e o trabalho irá causar problemas.

Então, em geral, minhas perguntas são:

Alguém tem alguma experiência em dividir grandes projetos? Existem dicas / truques que você gostaria de compartilhar?

  • Interfaces e injeção de dependência são seus amigos.
  • O livro de Michael Feather sobre como lidar efetivamente com o código legado é uma leitura obrigatória.
  • Os testes de integração são seus amigos
  • Dividir os subprojetos em foo-api (somente interfaces!) E foo-core e ter módulos apenas dependem do foo-api ajuda bastante e reforça a separação
  • Os módulos Jboss e / ou OSGi podem ajudar a reforçar a separação limpa

Que impacto isso teve no seu desenvolvimento e nos tempos de construção?

Impacto muito pequeno nos tempos de desenvolvimento e construção - um enorme ganho de tempo para o nosso pipeline de entrega contínua geral.

Que conselho você poderia oferecer para estruturar essa ruptura de um projeto?

Faça as pequenas coisas direito e as grandes coisas tendem a cair de forma limpa depois. Portanto, divida as coisas pouco a pouco - não faça uma reestruturação massiva de todo o lote, a menos que você tenha uma alta porcentagem de cobertura com seus testes de integração.

Escreva testes de integração antes da divisão - você deve obter mais ou menos os mesmos resultados após a divisão.

Desenhe diagramas da modularidade que você tem agora e onde deseja chegar. Crie etapas intermediárias.

Não desista - agora alguns barbeadores Yak constroem as bases para poder "construir e refatorar rapidamente sem medo" Plug Shameless -> de Ben e I, The Well-Grounded Java Developer .

Boa sorte!

Martijn Verburg
fonte
0

Minha opinião sobre isso é apenas separada quando necessário. No entanto, isso traz a seguinte pergunta: como determino se é necessário?

  • Quando o artefato for diferente (por exemplo, não construa o EAR, WAR, JAR, jar de teste de integração no mesmo projeto).
  • Quando, durante a separação, você percebe que algum código precisa existir em mais de um artefato, refatore-o para um novo.
  • Quando algum outro projeto fora da sua equipe deseja usar algo que você tem no seu projeto.

Um dos motivos é que os desenvolvedores precisam lidar com vários projetos e tentar descobrir além de qual pacote Java é a qual projeto ele deve pertencer. Estive em projetos nos quais isso se tornou realmente anêmico e tinha um projeto que tinha apenas quatro classes implementando uma funcionalidade apenas porque essa é a direção da arquitetura. Isso também aconteceu antes que o Maven fosse popular, de modo que os caminhos da classe e o que não precisam ser configurados nos scripts IDE e Ant.

A outra razão é que você teria mais peças móveis para lidar em termos de encanamento dos arquivos e provavelmente teria espaguete de dependência antes que perceba.

A refatoração também é mais fácil dentro de um projeto do que entre projetos.

Se o tempo de compilação for um problema, forneça um hardware melhor.

Archimedes Trajano
fonte