Separare i progetti Java


12

Ho un grande progetto Java e usiamo Maven per il nostro ciclo di costruzione. Questo progetto è ampiamente utilizzato - in altri progetti, in varie applicazioni, alcuni dei quali sono contenuti in esso e altri che sono altrove ... Ad essere sinceri, è un po 'un casino (vari bit aggiunti in momenti diversi per specifici scopi) e vorrei ripulirlo un po '. Inoltre, non è completamente testato (molti bit sono stati aggiunti senza un corretto test di unità e integrazione) e ci sono alcuni test che richiedono molto tempo per essere eseguiti o che in realtà non passano ... (uh-oh) - quindi i test sono disattivati ​​nel ciclo di costruzione di Maven (di nuovo, uh-oh).

Sto pensando di separare questo grande progetto in progetti specifici più piccoli, in modo tale che il sottoprogetto "finale" (o diversi sottoprogetti) raccolga i vari sottoprogetti di cui avrebbe bisogno.

Il mio pensiero è il seguente:

  • se separo il grande progetto in vari sottoprogetti, ciò chiarisce qual è la responsabilità di ciascun progetto.
  • separandomi in sottoprogetti, posso quindi ripulire i test di ciascun sottoprogetto singolarmente e attivare i test per quel sottoprogetto nel ciclo di costruzione di Maven.

Sono leggermente preoccupato per l'impatto che questo potrebbe avere sul tempo di costruzione.

  • L'imposizione di una struttura al grande progetto (ovvero in sottoprogetti più piccoli) rallenterebbe il compilatore?

Inoltre, ho una leggera preoccupazione sull'impatto che questo potrebbe avere sui tempi di modifica degli IDE (utilizziamo principalmente Intellij). Intellij sembra costruire ogni progetto a turno attraverso l'albero delle dipendenze - cioè se C dipende da B dipende da A, e cambio A, non tenterà di costruire B a meno che A non venga compilato, e così via. Probabilmente è vantaggioso, ma ho scoperto che se, ad esempio, cambio un'interfaccia in A ampiamente utilizzata in B e C, ci vuole del tempo per correggere tutti gli errori da quella modifica ...

Un'altra domanda è come utilizzare le classi di fabbrica. Alcuni aspetti del progetto dipendono da vasi esterni. Occasionalmente (per fortuna non spesso) questi vengono aggiornati e dobbiamo migrare. Tendiamo a gestirlo usando una classe Factory che punta alle versioni corrette del codice esterno (quindi non dobbiamo cambiare tutte le implementazioni attraverso la base di codice).

Al momento questo è tutto nel grande progetto, ma sto pensando che passando a sottoprogetti, potrei sviluppare un nuovo progetto per l'implementazione di un nuovo codice esterno, assicurarmi che il sottoprogetto sia completamente funzionale e testato, e quindi cambiare le dipendenze / la classe di fabbrica nel progetto utente. Tuttavia, ciò è reso più complicato dall'ampio uso di interfacce in tutto il grande progetto. Per esempio

  • sottoprogetto A - contiene interfacce
  • sottoprogetto B - dipende da A per le interfacce e il vecchio vaso esterno
  • sottoprogetto C - dipende da B (e quindi da A e dal vecchio vaso esterno) e contiene una classe Factory che utilizza le implementazioni di interfacce di B

Se devo cambiare il vaso esterno di B, posso:

  • creare un sottoprogetto B_ii - dipende ancora da A, e ora dal nuovo vaso esterno
  • una volta pienamente funzionante, posso aggiungere la dipendenza di C a B_ii e cambiare la classe Factory per usare le nuove implementazioni delle interfacce.
  • quando tutto funziona, posso quindi rimuovere la dipendenza di C dall'originale B e, se lo si desidera, rimuovere il sottoprogetto B.

  • È un modo sensato di farlo?

Quindi, in generale, le mie domande sono:

  • Qualcuno ha esperienza di rottura di grandi progetti? Ci sono suggerimenti / trucchi che vorresti condividere?
  • Che impatto ha avuto sul tuo sviluppo e sui tempi di costruzione?
  • Che consiglio potresti offrire sulla strutturazione di una simile rottura di un tale progetto?

Si noti che è possibile creare un progetto principale con A, B e C estratti separatamente l'uno accanto all'altro. Ciò consente ad esempio a IntelliJ di avere tutti e tre in memoria contemporaneamente e di eseguire i refactoring oltre i limiti del modulo.
Thorbjørn Ravn Andersen,

Risposte:


10

Ho intenzione di fare un primo taglio veloce a questo (ottimo Q BTW!):

L'imposizione di una struttura al grande progetto (ovvero in sottoprogetti più piccoli) rallenterebbe il compilatore?

Non abbastanza che conta, l'overhead è in realtà nelle invocazioni di Maven.

Inoltre, ho una leggera preoccupazione sull'impatto che questo potrebbe avere sui tempi di modifica degli IDE (usiamo principalmente Intellij). Intellij sembra costruire ogni progetto a turno attraverso l'albero delle dipendenze - cioè se C dipende da B dipende da A, e cambio A, non tenterà di costruire B a meno che A non venga compilato, e così via. Probabilmente è vantaggioso, ma ho scoperto che se, ad esempio, cambio un'interfaccia in A ampiamente utilizzata in B e C, ci vuole del tempo per correggere tutti gli errori da quella modifica ...

IDE diversi hanno i loro diversi punti di forza per quanto riguarda i binding Maven e la gestione delle dipendenze. Lo stato attuale sembra essere che funziona principalmente solo sugli ultimi Eclipse, Netbeans e IntelliJ - ma dovrai insegnare ai tuoi sviluppatori il doppio errore di emergenza di "Aggiorna i file sorgente dal disco e ricostruisci tutti i relativi progetti maven".

Trovo che devo farlo sempre meno in questi giorni però. Avere un disco SSD fa una grande differenza qui BTW.

tagliare i paragrafi delle classi di fabbrica

La gestione delle dipendenze è incredibilmente importante, indipendentemente da quale tecnologia (Maven / Ivy / qualunque) usi per aiutarti a implementarla.

Vorrei iniziare estrapolando i rapporti dal plugin di dipendenza Maven e fare un bilancio di ciò che hai. In generale, si imposta la dipendenza nella gestione delle dipendenze del POM il più in alto possibile nella catena alimentare, ma non più in alto. Quindi, se due dei tuoi sottomoduli usano una dipendenza esterna, trasferiscili nel loro POM genitore e così via e così via.

L'aggiornamento dei JAR esterni dovrebbe sempre essere eseguito come un mini-progetto appropriato. Valuta perché stai aggiornando, modifica il codice sorgente per trarre vantaggio da eventuali nuove funzionalità / correzioni di errori, ecc. Basta sbattere la versione senza questa analisi e il lavoro ti metterà nei guai.

Quindi, in generale, le mie domande sono:

Qualcuno ha esperienza di rottura di grandi progetti? Ci sono> suggerimenti / trucchi che vorresti condividere?

  • Le interfacce e l'iniezione di dipendenza sono i tuoi amici.
  • Il libro di Michael Feather su come gestire efficacemente il codice legacy è assolutamente da leggere.
  • I test di integrazione sono tuoi amici
  • Dividere i progetti secondari in foo-api (solo interfacce!) E foo-core e avere moduli dipendenti solo da foo-api aiuta molto e rafforza la separazione
  • I moduli Jboss e / o OSGi possono aiutare a rafforzare la separazione pulita

Che impatto ha avuto sul tuo sviluppo e sui tempi di costruzione?

Impatto molto minore sui tempi di sviluppo e costruzione: un enorme guadagno di tempo per la nostra pipeline di consegna continua complessiva.

Che consiglio potresti offrire sulla strutturazione di una simile rottura di un tale progetto?

Fai le piccole cose giuste e le cose grandi tendono a cadere in modo pulito dopo. Quindi dividi le cose un po 'alla volta - non fare una massiccia ristrutturazione dell'intero lotto a meno che tu non abbia un'alta percentuale di copertura con i tuoi test di integrazione.

Scrivi i test di integrazione prima della divisione: dovresti ottenere più o meno gli stessi risultati dopo la divisione.

Disegna diagrammi della modularità che hai adesso e dove vuoi arrivare. Progettare passaggi intermedi.

Non arrenderti - un po 'di rasatura Yak ora crea le basi per essere in grado di "costruire e refacturing rapidamente senza paura" Spina senza vergogna -> da Ben ed io The Well-Grounded Java Developer .

Buona fortuna!


0

La mia opinione su questo è separata solo quando necessario. Tuttavia, ciò porta alla domanda successiva: come posso determinare se è necessario?

  • Quando l'artefatto è diverso (ovvero non creare EAR, WAR, JAR, integrazione-testing-jar nello stesso progetto).
  • Quando durante la separazione noti che un codice deve esistere in più di un artefatto, trasformalo in uno nuovo.
  • Quando qualche altro progetto al di fuori del tuo team vuole usare qualcosa che hai nel tuo progetto.

Uno dei motivi è che gli sviluppatori devono occuparsi di più progetti e provare a capire a parte quale pacchetto Java è a quale progetto dovrebbe appartenere. Ho partecipato a progetti in cui è diventato davvero anemico e ho avuto un progetto che aveva solo quattro classi che implementavano una funzionalità solo perché quella è la direzione dell'architettura. Questo è accaduto anche prima che Maven fosse popolare, quindi i percorsi di classe e ciò che non deve essere impostato su entrambi gli script IDE e Ant.

L'altra ragione è che avresti più parti mobili da affrontare in termini di idraulica dei file e probabilmente avresti spaghetti di dipendenza prima di conoscerlo.

Il refactoring è anche più semplice all'interno di un progetto piuttosto che tra i vari progetti.

Se il tempo di compilazione è un problema, basta fornire un hardware migliore.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.