papo we(b)log

software engineering slave!
posts - 29, comments - 49, trackbacks - 26

OO design, domain objects e presentation layer

Eccomi di nuovo qui a snocciolare i dettagli e le infinite problematiche di un'applicazione reale. Non basta per sentirsi "al sicuro" l'aver con fatica imparato a strutturare i vari livelli seguendo tutti i principi di buona progettazione che riesci a ricordare a memoria! E nemmeno essere riusciti ad incollare in un'unica soluzione tecnologie "Off the Shelf" come OR mapper e container per l'IoC. Insomma... ecco il quesito.

Mi trovo per l'ennesima volta a riaffrontare costi e benefici legati alle varie soluzioni (che conosco) per la comunicazione tra business layer e strato della presentazione (in corsivo, perchè mi viene sempre da sorridere quando mi immagino oggetti software in carne ed ossa che assumo sembianze umane, di magnati del commercio incravattati e con doveroso sigaro in bocca mentre illustrano grafici di andamento delle azioni... sì, ho qualche problema).

L'architettura (la stessa di cui ho parlato fino ad ora nei miei post) è fatta così: uno strato di oggetti controllori dell'applicazione (che realizzano le regole dei vari casi d'uso), che lavorano su oggetti del dominio (un assembly privo di dipendenze, se non verso la libreria di validazione), mentre la persistenza è gestita da uno strato data access (wrapper di NHibernate). Questo per quanto riguarda la parte applicativa. I problemi iniziano quando invece ci affacciamo verso "il cliente", e parliamo di presentazione.

I client degli strati sopra descritti, o meglio, dello strato dell'applicazione, sono una serie di oggetti dell'interfaccia utente: l'unica supposizione che faccio è che questi oggetti siano locali rispetto all'esecuzione delle librerie business. Per locali intendo eseguiti sulla stessa macchina e nello stesso processo. I due esempi che ho (e che realmente utilizzo) sono pagine ASP.NET e casi di test di NUnit. Quindi niente accesso remoto. Questa considerazione mi servirà in seguito come ulteriore metro di giudizio.

Ho fatto in modo che l'accesso allo strato applicativo avvenga attraverso interfacce: un factory concreto (come BusinessFactory) a cui richiedere l'instanziazione dei controllori, definiti in termini di interfacce (ad esempio BusinessFactory.CreateHandler() che ritorna un oggetto di tipo IHandler). Quindi l'unico punto di accesso per le istanze dei vari handler è il factory concreto, che riesco a "rendere indipendente dall'applicazione" e quindi riutilizzabile grazie al container IoC (incapsulato dentro il factory). Il bello è che quindi posso riusare lo stesso client - la UI, come un itero sito web - senza ricompilare, e pluggare le regole di business da file di configurazione, specificando l'assembly con i controllori concreti. Molto elegante.

Ecco finalmente il punto chiave (e visto che da il titolo al post direi che è ora di affrontarlo!).

Come gestire la comunicazione nei due sensi tra:

  • UI >> Business >> Domain
  • Domain >> Business >> UI

Il punto cardine è che non voglio dare ai client - UI - accesso diretto alle funzionalità offerte dagli oggetti domain (metodi e setter). Il motivo è semplice: non voglio pormi il problema di un "cattivo utilizzo" da parte dei client, e fare in modo invece che tutte le modifiche avvengano tramite i controllori dell'applicazione.

Per ora ho risolto così (chiamiamola prima idea): per gli oggetti del dominio che richiedono di essere "visti" nella  UI, definisco un insieme di interfacce con un numero ridotto di properties (in sola lettura), e nessun metodo, corrispondenti alle cartteristiche che voglio rendere visibili. Le classi concrete del domain model implementano tali interfacce (che risultano quindi un sottoinsieme dell'interfaccia - l'elenco metodi - dichiarata nelle classi domain).

Tutta l'attività dei controllori è svolta in termini di queste interfacce: parametri dei metodi e tipi di ritorno. La conversione tra oggetti dominio e interfacce (Business >> UI) è gratuita grazie al Liskov Sostitution Principle (o chiamiamolo come si deve: progettazione rivolti alle interfacce!). La conversione inversa invece, Business >> Domain, avviene con un cast (o con un Find(objName) quando l'oggetto è "vicino" nell'albero della gerarchia degli oggetti - magari dopo spiego meglio).

La soluzione corrente non mi dispiace troppo, ma è comunque molto vincolante, non tanto per quanto riguarda il cast (anche se rende impossibile testare i controllori con oggetti Mock generati partendo dalle interfacce), ma per un altro problema molto più fastidioso: la dichiarazione delle interfacce degli oggetti del domain.

Infatti, per fare in modo che sia davvero utile tutta questa complessità (utile nel senso che mi porta un vantaggio rispetto al dare ai client - UI - accesso diretto agli oggetti del dominio) devo garantire che tutte le proprietà dichiarate nelle interfacce siano o di tipo primitivo (stringhe, interi, etc.) o altre interfacce del domain. E questo non è sempre possibile.

Quest'ultimo discorso potrebbe far venire in mente una soluzione analoga, per la quale cioè possono fare le stesse considerazioni (la seconda idea): usare i Data Transfer Objects. Parolaccia!

Non mi dilungo, visto che c'è un sacco di documentazione in rete. Arrivo direttamente al punto: non mi piace e non mi serve. Ricordate la premessa fatta all'inizio del post riguardo l'esecuzione locale del processo client e dei moduli business? Fowler spiega bene come i DTOs perdano valore in questo contesto, li chiama Local DTOs.
[a dimostrazione che non esistono soluzioni buone sempre... ma se qui risulta inapplicabile poichè cade la premessa fondamentale, per lo strato business remoto, incastonato in webservices, torna invece in auge].

Oggi pomeriggio ho provato a pensare se esiste una terza idea: in sostanza però ho cercato qualche workaround lessicale del C# per fare in modo di superare l'ostacolo che non mi permette di ridefinire il tipo di una proprietà (da interfaccia a oggetto domain). Non ho avuto troppo successo.

E qui entrate in ballo voi! Sono tutt'orecchi.

A presto.
-papo-

Print | posted on giovedì 20 aprile 2006 01:17 |

Powered by:
Powered By Subtext Powered By ASP.NET