2009-03-13 29 views
5

Una complessità molto comune per gli architetti tecnici è quella di dividere l'applicazione in assiemi e spazi dei nomi.Suggerimenti strategici su partizionamento di assiemi e spazi dei nomi

  • Gli assembly possono essere partizionati in base a: implementazione, prestazioni e limiti di sicurezza.
  • Gli spazi dei nomi possono essere partizionati in base ai limiti delle applicazioni logiche.

Inoltre: gli spazi dei nomi possono estendersi su più assiemi.

Ho avuto una brutta esperienza in un progetto una volta dove abbiamo partizionato gli assembly in base alle unità logiche dell'applicazione. Questa decisione si è conclusa con i file delle soluzioni con 30 o 40 progetti! Il tempo di caricamento del file della soluzione master era di ca. 5 minuti!!! Questo finì in una grande perdita di tempo, pff ...

Lo scenario opposto era quello di contenere tutto il codice in 1 assemblaggio e partizione quando è veramente necessario.

Avete ulteriori suggerimenti o best practice riguardanti questo problema?

risposta

1

Ho diviso il codice in assiemi separati solo quando ho bisogno di riutilizzarlo per due diverse applicazioni (praticamente). Così comincio con tutto in un unico progetto, e quando diventa evidente la necessità di riutilizzare il codice, creo un nuovo assembly e sposto il codice (a volte è ovvio fin dall'inizio, ad esempio quando è necessario disporre di un'app Web e vincere moduli facendo lo stesso cosa).

Re. Namespace, preferisco averlo partizionato abbastanza bene all'interno di un assembly, quindi è chiaro dove ogni classe appartiene e per cosa dovrebbe essere usata.

+0

Questo è anche il mio attuale modo di lavorare. Spesso vedo gli sviluppatori dividere l'app in 3 assembly perché la maggior parte di loro pensa che una 3 stratificazione in 3 assembly sia una buona cosa da fare; ma questo può essere ottenuto anche mediante la stratificazione dello spazio dei nomi e mantenere tutto il gruppo in 1. Solo quando la sicurezza e le prestazioni sono giocatori chiave, quindi inizia a studiare come parare fisicamente la tua app. –

0

È possibile suddividere le classi con Namespace e utilizzare le cartelle se si desidera unire i file di origine gruppo per semplificare la manutenzione. Se si dispone di requisiti di sicurezza e alcuni assembly devono passare attraverso un'elaborazione speciale come l'Obfuscation, ad esempio, potrebbe essere necessario separarli in un progetto separato.

La riutilizzabilità è anche un fattore che potrebbe essere necessario considerare quando si pensa se un'unità logica deve ottenere il proprio progetto poiché potrebbe essere necessario anche questo progetto in un'altra soluzione.

0

Ciò che ha funzionato bene per me è il raggruppamento a livello di grandi dimensioni per tipo di codice, ma più a livello di macro rispetto alle unità logiche con cui si stava dividendo. Ad esempio,

  • Motore: qualsiasi cosa non visiva. Classi di supporto, codice dell'infrastruttura di base. Tutto si riferisce a questo.
  • EngineUI: si basa su Engine e, ovviamente, è utilizzato per l'interfaccia utente, ma non è specifico per nessuna app.
  • EngineServer: si basa sul motore, utilizzato dalla build del server (di solito Web).
  • AppCore - Funzionalità principali specifiche dell'app, nessuna interfaccia utente.
  • AppUI - UI specifica dell'applicazione
  • AppClient - Utilizza AppUI, AppCore, EngineUI, Engine. L'app client reale.
  • AppServer: utilizza AppServer, EngineServer, Engine. L'app del server.

Ogni progetto ha una gerarchia di spazi dei nomi e una volta ogni tanto trovo il suo utile a sborsare un grande corpo di codice in un altro assieme ma di solito questi mantenere le cose ragionevolmente organizzato e gestibile, anche quando ci sono molte centinaia di file coinvolti. Troppi progetti sono sicuramente qualcosa che cerco di evitare. Un vantaggio di mantenere questi progetti al minimo è che rende molto più facile riutilizzare queste librerie in progetti successivi (come build di test automatizzati).

Non mi preoccupo troppo del codice inutilizzato distribuito da queste librerie perché posso usare un utility che rimuove le funzioni inutilizzate per la build finale, mantenendo le dimensioni del file al minimo.

0

Trovo utile organizzare gli spazi dei nomi in una gerarchia e rendere i nomi degli assembly corrispondenti allo spazio dei nomi a cui hanno contribuito. Ad esempio, un progetto chiamato Womble avrebbe un namespace di primo livello Womble, e quindi ci potrebbe essere assiemi chiamati:

Womble.ClientLibrary.dll 
Womble.Controls.dll 
Womble.Util.dll 
Womble.Interop.dll 

Qui, lo spazio esterno Womble campate più assembly, ma ciascun gruppo ha un subnamespace unico che solo può contribuire a, che trovi rimuovendo .dll dalla fine. Rende molto più facile ricordare cosa è necessario fare riferimento e trovare le cose.

Come per i numeri molto grandi di assiemi, in definitiva non è necessario tenerli tutti in un'unica soluzione. Nello sviluppo su larga scala aiuta a suddividere un grande prodotto in sottosistemi, che possono essere composti da più assiemi, e ciascun sottosistema può essere gestito da team separati e può avere i propri file di soluzione. Le varie squadre "rilasciano" le nuove versioni l'una dall'altra tramite il controllo del codice sorgente, trattandosi reciprocamente come librerie di terze parti.

Non penso che esista un modo difficile e veloce per decidere come suddividere il software in assembly. C'è un principio generale qui: le cose che cambiano per ragioni diverse dovrebbero essere separate.

I progetti di grandi dimensioni possono trarre vantaggio dal fatto che inserendo elementi in assiemi separati, è possibile correggerli separatamente. È possibile produrre un hotfix per un problema in Womble.Interop.dll e quindi produrre separatamente un hotfix per un problema in Womble.Controls.dll e fornire entrambi allo stesso cliente, in modo tale che in teoria questi due assembly potrebbero essere completamente mantenuto e supportato da team separati, senza dover coordinare direttamente le proprie attività.

Separa anche gli assembly creano chiarezza nelle dipendenze tra codice. Puoi vedere ad un livello molto alto (guardando solo l'elenco dei riferimenti) come una porzione di codice dipende da un'altra e come potrebbe essere riutilizzata. Se metti tutto in un unico assemblaggio, potrebbe essere un groviglio ingarbugliato senza alcun modello ragionevole.

0

motivazione

Il motivo per cui sto inviando questa risposta tardiva è che tutte le risposte precedenti sono più raccomandazioni migliori pratiche piuttosto che coerenti.

Da molti anni sto lavorando a un progetto .NET di grandi dimensioni e solo le migliori pratiche coerenti che sono sia strategicamente sia tecnicamente ben motivate è quella proposta dal team di NDepend.

In poche parole

consigli NDepend sono generalmente in linea con l'esperienza di non strutturare complessi secondo l'architettura.Contiene anche considerazioni su quando avere assiemi separati e perché. La regola empirica è utilizzare la strutturazione tramite gli assembly per motivi fisici, utilizzare i namespace per motivi logici.

NDepend sintesi delle migliori pratiche:

Per gli assiemi e progetti

  • ridurre drasticamente il numero di assemblee di base di codice.
  • Creare un nuovo assieme solo quando ciò è giustificato da un requisito specifico per la separazione fisica.
  • In un progetto di Visual Studio, utilizzare "riferimento per assembly" anziché "riferimento per progetto Visual Studio".
  • Non utilizzare mai l'opzione di riferimento di Visual Studio 'Copia locale = True'.
  • Inserire tutte le soluzioni VS e creare file .bat di azione in una directory $ rootDir $.
  • Compilare tutti gli assembly nelle directory: $ rootDir $ \ bin \ Debug e $ rootDir $ \ bin \ Release
  • Utilizzare la directory $ rootDir $ \ bin per ospitare gli assembly dei test.

Per gli spazi dei nomi

  • utilizzare il concetto di spazio dei nomi per definire i confini di componenti.
  • Uno spazio dei nomi contiene in genere da una a due dozzine di tipi e ha una dimensione ragionevole che rientra nell'intervallo da 500 a 2.000 LoC.
  • Prenditi il ​​tempo necessario per livellare i componenti del tuo codice base, è sicuramente un compito più economico del previsto, quindi il Return On Investment sarà alto.
  • Controllare continuamente che il grafico delle dipendenze dei componenti all'interno di un assieme sia aciclico.
  • Se un componente è troppo grande (> 2.000 LoC), quindi utilizzare sub-namespace per dividerlo in un insieme più piccolo di componenti correlati.
  • In qualsiasi scala, classificare i componenti tra mediatori di alto livello, funzioni indipendenti di medio livello, base/domini di livello inferiore.
  • La presenza di un set di componenti "livellati" elimina la necessità della maggior parte delle decisioni di progettazione.

lettura dettagliata

Partitioning code base through .NET assemblies and Visual Studio projects

Defining .NET Components with Namespaces