Design di classe per la scrittura di più versioni di più file

3

Sto scrivendo un servizio web in Java che legge alcune informazioni da un DB e genera più file JSON scritti su S3. Per ogni tipo di file, ho un POJO che è serializzato su JSON usando jackson.

Lo schema dei file può cambiare nel tempo: è possibile aggiungere nuovi campi, rimuovere campi esistenti, ecc. Qualsiasi modifica dello schema richiederà una nuova versione del file da creare. Pertanto, in qualsiasi momento, potrebbero esserci più versioni di un file in S3.

L'architettura attuale è piuttosto semplice - Esiste una classe Controller che chiama un paio di classi per ottenere dati dal DB. Dopo di ciò passa quei dati a una classe FileGenerator che crea più POJO e li scrive su S3 attraverso una classe di repository.

Devo modificare questo design per renderlo più modulare e gestire più versioni di file.

Finora questo è quello che ho trovato:

  • Una classe generatore per generare ogni tipo di file (o POJO) (File1Generator, File2Generator ecc.)

  • Una classe FileManager per ogni tipo di file, che chiama il generatore e serializza il POJO e quindi lo scrive nell'archivio dati. (File1Manager, File2Manager ecc.)

  • La classe Controller ora raggrupperà tutti i file manager uno per uno.

Tuttavia, non sono sicuro di come progettare il sistema in modo da poter gestire più versioni dei file. Per i POJO, devo avere un POJO distinto per ogni versione: File1POJO_V1, File1POJO_V2. Ma poi avrò anche generatori aggiuntivi e gestori aggiuntivi. Le informazioni trasmesse ai generatori possono variare da una versione all'altra. Lo stesso con i gestori. Sto facendo fatica a creare una buona gerarchia di classi tale che il codice sia facilmente estensibile. Di seguito è riportato uno scheletro del codice, come ho pensato:

class File1POJOV1{
 private String prop1;
 private String prop1;
 ..........
 public setProp1(){}
 public setProp2(){}
}

class File1Generator_V1{
  public File1POJOV1 createFile(String prop1, String prop2 ...){
        //Perform manipulations and create File1POJO_V1
  }
}

class File1Manager_V1{
  private File1Generator_V1 fileGenerator;
  private Serializer serializer; //JSON serializer
  public void createFile(String prop1, String prop2,.., String directory){
      File1POJOV1 file = fileGenerator.createFile(prop1, prop2 ...);
      byte[] data = serializer.serialzer(data); 
      String path  = generateFilePath(); //dynamic based on properties
      writeToS3(data, path, directory); //write data to S3 bucket
  }
}


class Controller{
   File1Manager_V1 manager1_V1;
   File2Manager_V1 manager2_V1;
   generateFile(String prop1, prop1..., directory){
      manager1.createFile(prop1, prop2..,directory);
      manager2.createFile(prop3, prop4.. directory) 
   } 
}

Il problema è che quando c'è una nuova versione di un file, ci sarà un POJO separato per quello che avrà più (o meno) campi rispetto alla versione precedente. Questa modifica verrà propagata ai Generatori e ai Manager man mano che i parametri di input dei metodi cambieranno. Non sono in grado di capire come avere un design elegante per questo sistema che semplifica la manutenzione.

Modifica : Ho alcuni buoni suggerimenti per non rimuovere campi nelle versioni successive di un file. Ma la mia domanda rimane: supponendo che non rimuovo i campi e che ogni nuova versione sia una sottoclasse di quella precedente, dovrò comunque scrivere un generatore separato e un gestore per ciascuna sottoclasse. Questo può essere evitato? Quale sarebbe un design migliore?

    
posta vjain27 19.10.2015 - 04:22
fonte

2 risposte

4

Mantieni la necessità di versioning basso

Presumo qui che i file JSON vengano generati per un altro componente / servizio / client da consumare. Quindi prova a non rimuovere i campi. Se si aggiungono solo nuovi campi, non è necessario creare una nuova versione, a condizione che i consumatori dei file JSON ignorino i campi che non conoscono.

Un'interfaccia in cui i consumatori ignorano le funzionalità che non conoscono è più solida. Immaginate ad esempio se consumo i vostri dati in V0 e poi per un anno aggiungete nuovi campi su base bisettimanale, arrivando a V26 dopo un anno. Quindi in V27 si aggiunge un campo che voglio consumare anche. Devo essere disturbato aggiornando il mio codice per gestire i campi aggiunti tra V1 e V26 anche se non li uso? Io non la penso così

Rimuovere i campi d'altra parte è una bestia diversa. Dovresti farlo raramente, e alla rinfusa. L'idea di versioni principali e secondarie in versioning semantico riguarda proprio questo. Se aggiungi qualcosa, è una versione secondaria e non dovrebbe influire sugli utenti, se rimuovi le cose, è un aggiornamento importante, che può rompere il codice dipendente.

Questo coincide anche con la nozione di sottotipi, polimorfismo e sostituibilità . In sostanza, per aggiungere un nuovo campo a FirstPOJO , puoi modificarlo oppure puoi sottoclassi a SubPOJO , che estende FirstPOJO aggiungendo someField . Il codice scritto contro FirstPOJO sarà in grado di gestire SubPOJO in modo trasparente. Ovviamente se inizi a rimuovere le cose, il codice può rompersi.

So che questo non risponde esattamente alla tua domanda. Ma il tuo problema di base è che hai un'architettura di codice, che non scala. Riducendo la necessità di ridimensionare in primo luogo, elude il problema.

    
risposta data 22.10.2015 - 14:36
fonte
0

Penso che la risposta dipenda da ciò che stai cercando di ottenere oltre a scrivere file.

Per il momento lasciamo fuori i POJO, leggete i dati da un DB e scriveteli in un formato di file specifico tramite la classe fileManger. La lettura DB restituisce alcuni tipi di lettori di dati generici, quindi finché esistono le colonne necessarie, si sta bene.

Se vuoi scrivere gli stessi dati DB in un formato diverso, puoi creare un'altra classe fileMangerV2 e sei a posto.

Se vuoi aggiungere campi al DB, di nuovo stai bene.

Se rimuovi i campi, le cose si interrompono e hai un problema di compatibilità con le versioni precedenti non puoi andare in giro se vai avanti e cancelli quei dati.

Quindi questo riguarda davvero i dati sottostanti. Se questo è lo stesso in tutte le versioni dei file, è sufficiente l'unico POJO che corrisponde a quella struttura dati e un fileManager per file Formato per gestire la lettura e la scrittura in quel formato

Tuttavia, se si desidera esporre il formato del file per codificarlo con un POJO corrispondente e la struttura dei dati sottostanti è la stessa, è possibile creare nuove classi che ereditano (se non si mente esponendo tutte le proprietà della classe base) o avvolgere il POJO di base

La conversione da un POJO all'altro è quindi relativamente semplice, basta esporre la classe base sottostante e questo codice può essere incluso nell'oggetto stesso piuttosto che nel fileManager. Questo ti permetterà di avere una singola classe fileManager che è in grado di generare qualsiasi formato di file, sia per riflessione, o meglio, facendo in modo che tutti i POJO implementino un'interfaccia comune ICanBeWrittenToAFile che espone GetFieldsForWritingToFile () o qualcosa di simile.

I tuoi controller e fileManager possono quindi prendere un ICanBeWrittenToAFile come input piuttosto che un POJO specifico e devi solo iniettare l'unico fileManager

Ora dì che hai qualche logica di business X che vuoi essere in grado di eseguire sui dati indipendentemente dalla versione del file. Puoi estendere lo stesso concetto facendo in modo che i POJO implementino ICanPerformLogicX ecc.

    
risposta data 22.10.2015 - 13:56
fonte