Qual è il modo più efficace per condividere il codice tra le applicazioni .NET?


15

Nel nostro lavoro, abbiamo diverse applicazioni .net che condividono molte funzionalità di base. Abbiamo creato queste applicazioni usando un'architettura n-tier pulita, ma abbiamo colto quel momento in cui ci rendiamo conto che abbiamo implementato nuovamente le stesse funzioni diverse volte. Ovviamente questo viola DRY e vorremmo correggerlo. Stiamo già utilizzando Nuget con successo per un comune codice di colla (cablaggio IoC, registrazione, impostazioni), ma vorremmo anche condividere i nostri dati e livelli aziendali tra tutte le nostre applicazioni. L'idea è che l'interfaccia utente gestirà solo le parti del livello aziendale di cui ha effettivamente bisogno.

Questo sembra inizialmente un problema diretto, ma lo sviluppo in corso potrebbe fornire alcune insidie ​​e non siamo sicuri di come procedere. Diciamo che facciamo il nostro One Business Layer per dominarli tutti. Per brevità, la chiamerò "Fondazione". Effettuiamo il porting delle nostre applicazioni per utilizzare la Foundation e tutto procede alla grande. La Fondazione è distribuita per illuminare i livelli dell'interfaccia utente tramite nuget e stiamo bene. Ma poi iniziamo ad aggiungere funzionalità alle nostre applicazioni e ci troviamo in difficoltà.

Diciamo che stiamo lavorando al Progetto A e aggiungiamo una nuova funzionalità che richiede modifiche a Foundation. Apportiamo le modifiche alla fondazione (Foundation-A) e le inviamo al feed nuget come pacchetto instabile. Il progetto A ottiene l'ultimo pacchetto nuget e tutto va bene. Nel frattempo, un altro sviluppatore sta lavorando al Progetto B. Ottiene l'ultima Fondazione dal controllo del codice sorgente, ma la prende da un ramo stabile, in modo che non vi siano modifiche al Progetto A. Apporta modifiche e crea Foundation-B. E va tutto bene. Ma poi scopriamo quella funzionalità di implementazione di Foundation-A e Foundation-B che potrebbe effettivamente condividere il codice, quindi li combiniamo. Nel frattempo Foundation-C sta fluttuando là fuori con i suoi cambiamenti. Alla fine, Foundation-B è pronto per la produzione, quindi lo spingiamo fuori. Ma poi dobbiamo aggiornare la produzione A, B,

Sembra che potrebbe funzionare, ma siamo preoccupati di lavorare con schemi di database diversi e di mantenere tutto sincronizzato tra i vari rami del repository Foundation e i repository Project A, B e C. Sembra che probabilmente ci vorrà molto lavoro manuale, il che apre la possibilità di errori. Vorrei che il più automatizzato possibile.

Ecco lo stack che stiamo usando: C #, TFS con integrazione continua, Nuget. Le nostre applicazioni sono tutti i vari tipi di applicazioni ASP.NET. Siamo disposti a esaminare diversi SCM se ciò renderà le cose più facili.

Sto cercando modi per mantenere Nuget sano con i nostri diversi rami del codice sorgente. Non vogliamo spingere accidentalmente il codice di sviluppo nella produzione perché facciamo riferimento al pacchetto Nuget sbagliato.


Inoltre, se questo non è un buon forum per una discussione come questa, qualcuno può suggerirne uno migliore?
Josh,

Risposte:


9

Apportiamo le modifiche alla fondazione (Foundation-A) e le inviamo al feed nuget come pacchetto instabile.

Ecco dove inizia il tuo problema ... Non farlo.

Qualsiasi modifica a Foundation v1.0 dovrebbe essere intrinsecamente utile per tutti i consumatori di Foundation, altrimenti non appartiene a Foundation. Quindi, quando si crea il pacchetto nuget, farlo come una versione ufficiale e stabile di Foundation (cioè v1.1), o non farlo affatto.

Il Progetto B dovrebbe costruire i suoi miglioramenti alla Fondazione come farebbe normalmente, ma (in un buon modo di gestione delle fonti) dovrebbe fondersi con i cambiamenti del tronco (v1.1) prima di spingere una Fondazione stabile (v1.2) in nuget.

Altri progetti che possono utilizzare i miglioramenti di Foundation possono aggiornare i loro riferimenti di nuget quando appropriato, o attenersi alle versioni precedenti, se necessario.

Sono d'accordo con @Giedrius ; questo mi sembra essere più un problema di controllo / ramificazione del codice sorgente, nel senso che se la diramazione / fusione di Foundation viene gestita correttamente, i problemi di gestione dei pacchetti diventano discutibili.


Sì, questo ha senso. Il problema è nell'applicazione pratica. Mentre esegui gli aggiornamenti a Foundation per la versione 1.1, desideri utilizzare tali modifiche nel Progetto B durante lo sviluppo. Se il modo in cui hai condiviso quel codice è tramite nuget, allora le tue opzioni sono: 1) Pubblica nuovo pacchetto nuget o 2) Copia manualmente dll in giro. Nessuna di queste sembra una buona scelta.
Josh,

Alcuni di questi possono essere mitigati con una suite di test efficace, ma con l'aggiunta di funzionalità ci sarà ancora avanti e indietro.
Josh,

@Josh Sì, gli aggiornamenti di Foundation dovrebbero essere completi e verificabili indipendentemente dal modo in cui il Progetto B li utilizzerà (dal momento che stanno entrando in una libreria condivisa), quindi "pubblicare un nuovo pacchetto nuget" è la strada naturale da percorrere. Non importare e utilizzare Foundation v.Next nel Progetto B fino a quando non è un pacchetto stabile in nuget. Ci vuole un po 'di disciplina, ma è molto meglio del disordine di farlo diversamente.
Eric King,

1
Capisco perfettamente quello che stai dicendo, ma non credo sia molto realistico. Man mano che sviluppi una funzionalità, anche se hai completamente testato tutta la logica aziendale e il livello di accesso ai dati, ci saranno delle modifiche prima che vengano portate in produzione. Forse ti rendi conto che devi aggiungere un'altra proprietà alla logica aziendale o il proprietario del prodotto ritorna con modifiche o chiarimenti. Forse alcune delle tue ipotesi semplificative erano sbagliate. Queste sono cose che accadono tutto il tempo. Se sei limitato al blocco in rilascio della tua logica di base, sembra davvero molto limitato.
Josh,

2
Dato che lo facciamo da alcuni mesi, questo è fondamentalmente ciò su cui abbiamo deciso. Penso di essere troppo preoccupato per avere un processo formale. Una volta che abbiamo iniziato a lavorarci davvero, le cose sono andate davvero bene. Grazie per il tuo contributo.
Josh,

4

Ricalcola il tuo codice duplicato in funzioni applicabili in modo più astratto e inseriscile nelle loro librerie o framework. Rendili vagamente accoppiati e indipendenti dall'architettura e non dovresti mai avere problemi. Se hai bisogno di ispirazione, studia come il framework .NET estrae concetti usando cose come interfacce, generici e modelli software come Dispose().

Inoltre, ricorda che non tutto il codice duplicato è realmente duplicato; in parte è un codice di colla o un codice che è altrimenti necessario per sostenere l'architettura, quindi non ossessionarti dall'essere troppo ASCIUTTO.


Forse non ero chiaro come avrei potuto essere nella mia domanda. Si tratta di ciò che verrà dopo. Hai fatto tutto questo e ora devi condividere il tuo codice in modo efficace tra i tuoi diversi progetti. Qualcuno dice "usa solo nuget per quello" e suona benissimo fino a quando non entri nei dettagli e ti imbatti in problemi - come come sincronizzare le diverse modifiche da diversi rami e applicazioni.
Josh,

Bene, sono d'accordo con Eric King. Il codice di base deve essere inviato come una versione stabile, non instabile. La chiave sta nel mantenere un'API stabile: se sai che le firme dei tuoi metodi di fondazione non cambieranno, puoi spingere i metodi in modo sicuro su Foundation e riformattarli in seguito senza interrompere nulla.
Robert Harvey,

4

Riutilizzo del codice

Ci sono stati diversi approcci al riutilizzo del codice che hanno trovato il favore nel corso degli anni. Ogni approccio ha il suo posto e, soprattutto, i suoi problemi , ma i modi possibili per riutilizzare il codice in .NET sono:

  1. Biblioteca comune Il codice necessario in più di un posto viene inserito in una libreria comune e tutte le altre parti della base di codice hanno quindi un unico riferimento a questo codice. Il principale svantaggio è che si finisce con la maggior parte del progetto a seconda di questa libreria che contiene molte funzioni non correlate. Questa è una cattiva idea dal punto di vista della garanzia della qualità.

  2. Codice sorgente comune. Il codice necessario in più di un posto viene scritto una volta e inserito in un file di origine in una struttura di directory comune. Tutti i progetti che richiedono questo codice includono quindi questo file come uno dei loro file di origine. Ciò fornisce il riutilizzo del codice e i vantaggi di scrivere una volta ne usano molti. Tuttavia. Il rovescio della medaglia è che diventa possibile avere parti diverse del progetto compilate con diverse versioni di questo codice - il che può introdurre alcuni sottili difetti che possono essere molto difficili da rilevare e identificare con l'assicurazione della qualità.

  3. Servizio. Il codice comune è implementato come un servizio a cui possono accedere altri aspetti. Questo ha il vantaggio che ci sarà un singolo servizio in una distribuzione ed evita le dipendenze. Tuttavia introdurrà latenza e guasti. Questo tipo di approccio funziona bene in prodotti distribuiti di grandi dimensioni in cui sono già compresi e gestiti l'alta disponibilità e la tolleranza agli errori.

Gestione di NuGET

Qui hai un problema molto più interessante. Gestione di più configurazioni. Il mio consiglio qui è di non gestire una base clienti diversificata con diverse versioni di codice, ma con file di configurazione. Esistono almeno 3 livelli di dati di configurazione da gestire. Configurazione del prodotto di base (interna) che il cliente non vede mai. Le opzioni di configurazione predefinite che il cliente può modificare e il livello finale di opzioni di configurazione che il cliente ha modificato. Separando questi diversi livelli sarai in grado di implementare gli aggiornamenti senza distruggere le configurazioni dei tuoi clienti.


1

Penso che il problema non sia nel nuget / controllo del codice sorgente / ramificazione, ma in ciò che scivola nel codice di incollaggio.

Robert ha una bella risposta, solo per vedere l'intero quadro, consiglio di pensare a quali dipendenze porteranno queste utilità comuni in ogni progetto:

http://ayende.com/blog/3986/let-us-burn-all-those-pesky-util-common-libraries http://blog.jordanterrell.com/post/CommonUtility-Libraries-Dead.aspx

Il modo migliore per evitare l'inferno è aprire il codice della colla. In questo modo inizierai a preoccuparti che nessuna logica aziendale diventerà pubblica, quindi nessuna dipendenza concreta dal progetto scivolerà nel codice della colla, che sarebbe abbastanza astratto da poter essere riutilizzato da chiunque, anche al di fuori della tua azienda - e se quella colla il codice sarà abbastanza buono - otterrai anche l'input della community.


Non sto parlando della gestione del codice colla. Abbiamo già una soluzione per questo e funziona bene. Sto parlando della condivisione della logica aziendale. Ad esempio, le nostre applicazioni si occupano della gestione di organizzazioni e persone. Quindi, dobbiamo creare una persona e ci sono un sacco di regole di business associate a come una persona dovrebbe essere creata. Invece di crearla più volte, vogliamo avere un pacchetto che gestisca tutto, dalla costruzione dell'oggetto alla persistenza che condividiamo tra i diversi progetti che hanno bisogno di creare persone.
Gios

0

La soluzione è semplice: crea un progetto separato e gestiscilo come cosa separata: requisiti, test, convenzioni, documentazione, pianificazione, persone, ecc. In questo modo, assicurati che la qualità rimanga alta e che possibili cambiamenti che si rompono valutato prima di creare un problema.

O ancora meglio: rendilo "open-source" all'interno della tua organizzazione. Quindi chiunque può modificare il codice, ma solo poche persone selezionate avranno diritti di commit completi. E quelle persone saranno responsabili di garantire qualità e caratteristiche corrette.

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.