Here we are, lets go deeper!

Concentriamo in questo post i primi 2 argomenti:

  • L’organizzazione della solution in Visual Studio, e i problemi che ci dobbiamo portare a casa;
  • La Shell: lo scheletro della nostra infrastruttura;

Visual Studio: how to…

L’organizzazione della solution in VS è fondamentale per non impazzire durante lo sviluppo e per supportare sia i vostri requisiti sia l’infrastruttura di IoC; quest’ultima è quella che rende particolarmente critica la struttura della solution:

Dogma: un framework non deve dipendere da un IoC container;

L’introduzione di IoC comporta che quello che prima veniva realizzato con un singolo progetto di Visual Studio adesso venga realizzato con 2 progetti (minimo):

  • Contracts: un progetto contiene i soli contratti (interface(s)) pubblici e sarà questo progetto/assembly a cui gli altri avranno una reference;
  • Runtime: un progetto contiene l’implementazione concreta dei contratti, questo progetto non è “noto” a nessuno se non alla configurazione del framework di IoC;

Questa separazione porta ad un problema:

image

L’applicazione ha una reference alla parte dei contratti ma a runtime avete bisogno che l’implementaziuone concreta dei contratti si trovi all’interno della “Bin” dell’applicazione… altrimenti scoppia tutto, ci sono svariate soluzioni a questo inghippo.

La più bella, ma anche complessa, passa dalla realizzazione di un Task per MSBuild e in questo caso non ha molto senso, forse… A noi basta semplicemente una “post build action” che copi il contenuto della Bin della parte “runtime” nella Bin dell’applicazione:

copy "$(TargetDir)$(TargetName).*" "..\..\..\..\out\$(ConfigurationName)\$(TargetName).*" /B /Y

In realtà come si può intuire dal comando non facciamo proprio quello ma passiamo da un punto comune. Ergo dall’assembly “Runtime” al termine della compilazione copiamo il tutto in una directory “out\Debug”, ad esempio, e poi da una post buil action dell’applicazione facciamo il resto:

xcopy "..\..\..\..\out\$(ConfigurationName)\*.*" "$(TargetDir)*.*" /Y /R

Questo doppio passaggio, nonostante all’apparenza si ridondante, risulta poi molto comodo perchè ci consente di avere la massima flessibilità nell’organizzazione su FileSystem della struttura della nostra solution, e vi garantisco che quando superate i 50 progetti la cosa diventa importante.

Perchè il tutto funzioni ci sono altri 2 vincoli, purtroppo:

  1. Dovete impostare manualmente le dipendenze (dx sulla Solution –> Project Depenedencies) al fine di garantirvi che i progetti vengano buildati nell’ordine corretto; se impostare le reference tra progetti questa cosa VS è in grado di farla da solo altrimenti, giustamente, no;
  2. C’è poi la vera rottura che ogni compilazione dovrà essere una Full Rebuild della solution altrimenti le Post Build Action non vengono sempre onorate… in questo caso un task di MS Build sarebbe la manna sempre perchè quando superate i 50 progetti ogni rebuild è un delirio…

La mia scelta per l’organizzazione dei progetti su FileSystem è questa:

image

Direi che non c’è nulla da spiegare, se avete domande fatele.

IoC Integration

Questa sezione non è strettamente necessaria, ma dipende molto dal vostro gusto personale e da quello che volete sfruttare di un framework di IoC.

Possiamo senza dubbio affermare che l’adozione di un framework di IoC tende ad essere virale per l’applicazione: con questo intendo dire che è decisamente arduo fare in modo che l’applicazione conosca un’astrazione del concetto di IoC al fine di rimpiazzare il toolkit.

La cosa è fattibile fintanto che del toolkit sfruttate “solo” (come se fosse poco) le funzionalità di Service Locator, Lifestyle management, Dependency Injection e poco altro… se però volete spingere al massimo il toolkit e scroccarne le funzionalità per fare AOP ad esempio allora non c’è nulla da fare astrarre il concetto è impossibile e l’adozione di un certo toolkit diventerà permanente.

Detto questo sta a voi decidere quale strada prendere. Ritengo che la scelta debba essere:

  • Sto sviluppando l’applicazione allora mi va benissimo dipendere da un framework xyz, non lo vedo come un problema, anzi…;
  • Sto sviluppando un framework/toolkit non posso permettermi nessuna dipendenza perchè obbligherei l’utilizzatore del toolkit/fx alla stessa dipendenza, quindi se ho bisogno di un fx di IoC (e probabilmente è uno smell) allora devo astrarre.

La Shell

Finalmente! :-D

La Shell è il guscio/conchiglia che ospita la nostra applicazione, la Shell è composta come minimo da:

  • Application: l’applicazione è l’entry point, l’eseguibile che viene effettivamente lanciato. Qui non c’è nessuna logica semplicemente l’applicazione ha l’onere di “trovare” un’istanza di…
  • ApplicationBootstrapper: l’appBootstrapper è il vero cuore dell’applicazione, i suoi compiti sono:
    • configurare il service container registrando i servizi della shell e i servizi che saranno esposti dalla shell;
    • avviare il processo di discovery/inizializzazione/esecuzione dei moduli;
    • monitorare gli eventuali messaggi diretti all’applicazione: tipico esempio è una richiesta di shoutdown;
    • Infine avviare la shell vera e prorpia;
  • La Shell è infine il componente visuale che ospiterà la nostra applicazione;

Inizialmente ero partito facendo anche della shell un modulo ma era un vero macello… del resto è evidente che la shell è un requisito. La struttura dei progetti nella solution è qualcosa del tipo:

  • MyApplication (WPF App Project);
  • MyApplication.System (Class Library):
    • definisce i contratti dei servizi esposti al nostro ecosistema;
  • MyApplication.System.Windows (Class Library):
    • definisce i servizi esposti all’ecosistema dipendenti da WPF, questo progetto può essere “mergiato” con il precedente con lo scotto che tutti i progetti a questo punto dipenderanno dall’infrastruttura di WPF. “Filosoficamente” è sbagliato;
  • MyApplication.Runtime (Class Library):
    • implementa i contratti di MyApplication.System e MyApplication.System.Windows, volendo qui potremmo avere la stessa separazione in Runtime e Runtime.Windows ma non è detto che ci serva/abbia senso;
  • MyApplication.Boot (Class Library)
    • è il bootstrapper;
    • questo signore conosce il framework di IoC, i contratti e le implementazioni concrete;
  • Toolkit (Class Library)
    • contiene contratti e implementazioni astratte delle classi di base per facilitare lo sviluppo;

Se volete dare un okkio alla struttura trovate il tutto qui: CompositeUI_v1.zip.

Per ora non fa proprio nulla se non:

  • Configurare il container;
  • Avviare l’applicazione e la Shell;

Se consideriamo per fare 2 stupidate ci sono 6 progetti nella solution direi che cominciamo bene. Per farlo funzionare assicuratevi che:

  1. Compili… :-D;
  2. Il Project Build Order sia quello giusto:
    1. destro sulla Solution –> Project Dependencies –> nella combo selezionate il progetto MyApplication e verificate che tutti gli altri progetti siano spuntati;

Se siete fortunati e il file .suo è arrivato a voi intatto dovreste anche avere un po’ di breakpoint già inseriti, un bel F5 e studiate cosa succede. Fin qui non è nulla di difficile anzi… sintetizzando quello che avviene è:

  1. Parte l’applicazione WPF;
  2. Nell’OnStart:
    1. recuperiamo, dietro le quinte via reflection, una reference all’IAppicationBootstrapper concreto;
    2. invochiamo il metodo Boot();
  3. Il Bootstrapper:
    1. per prima cosa istanzia e configura il container (Castle Windsor nell’esempio);
    2. chiede al container di risolvere il ViewModel della Shell;
    3. avvia la Shell;

Prossimo passo sarà quello di definire un sistema per “descrivere” al motore di boot quali sono i moduli installati.

Stay tuned… e non sparate sul pianista, o meglio magari non sparate troppo in basso :-P

.m

Allegati: CompositeUI_v1.zip.

Technorati Tags: ,