Ciao a tutti,
SCENARIO: presso un cliente, abbiamo installato una piccola applicazione che, tra le altre cose, gestisce una piccola rubrica telefonica. Ogni contatto della rubrica è modellato da una entità 'Contact' che ha un nome, un cognome, un indirizzo e-mail e quattro numeri di telefono. Come ORM ho usato NHibernate su un SQL Server (credo Express).
PROBLEMA: il cliente mi chiede di *integrare* la rubrica con una rubrica precedentemente esistente nel suo sistema informativo, residente su un altro server rispetto a quello su cui è installato il "nostro" db.
COME MI COMPORTO? Io avevo pensato di chiedere di poter avere nel nostro db una vista che mappasse la loro rubrica su uno schema simile a quello che usiamo internamente e poi usare eventualmente un mapping polimorfico. Può essere una via percorribile?
IN GENERALE: confesso di non avere grande esperienza nella realizzazione di sistemi che si integrassero con sistemi già esistenti. In questi casi, come vi muovete? Io - che sono una tra le ultime ruote del carro - ho come l'impressione che l'integrazione con la base di dati già esistente dovrebbe essere IL PRIMISSIMO STEP, prima del design del modello... sbaglio?
Ciao e grazie,
Giulio
--
Ciao,
Io in una situazione del genere ho agito solo sulla mia base dati. Probabilmente però il caso specifico era leggermente diverso. Nel mio caso quella che per te è la "rubrica già esistente" era per me una applicazione di importanza inferiore rispetto a quella che avevo realizzato io.La nuova applicazione copriva tutte le funzionalità della vecchia quindi non aveva senso continuare ad utilizzarle entrambe se non per un primo periodo di passaggio imposto dal cliente. Quindi abbiamo "sincronizzato" il nostro db con quello "vecchio" mantenendo la sincronizzazione per il periodo di passaggio alla nuova applicazione.ciao
Ciao 4Net,
4Net: La nuova applicazione copriva tutte le funzionalità della vecchia quindi non aveva senso continuare ad utilizzarle entrambe se non per un primo periodo di passaggio imposto dal cliente. Quindi abbiamo "sincronizzato" il nostro db con quello "vecchio" mantenendo la sincronizzazione per il periodo di passaggio alla nuova applicazione.
La nuova applicazione copriva tutte le funzionalità della vecchia quindi non aveva senso continuare ad utilizzarle entrambe se non per un primo periodo di passaggio imposto dal cliente. Quindi abbiamo "sincronizzato" il nostro db con quello "vecchio" mantenendo la sincronizzazione per il periodo di passaggio alla nuova applicazione.
Nel mio caso, la base dati del cliente continuerà a funzionare nei secoli dei secoli, quindi purtroppo questo non è uno scenario percorribile. Il problema di fondo è che io dovevo progettare la mia app a partire dalla loro base dati, e non cercare ora di fare un adattamento. Va be', speriamo di rimediare... :-/
Ciao Giulio,
petrux: IN GENERALE: confesso di non avere grande esperienza nella realizzazione di sistemi che si integrassero con sistemi già esistenti. In questi casi, come vi muovete? Io - che sono una tra le ultime ruote del carro - ho come l'impressione che l'integrazione con la base di dati già esistente dovrebbe essere IL PRIMISSIMO STEP, prima del design del modello... sbaglio?
Non è detto... IMHO un modello di dominio dovrebbe essere progettato per soddisfare al meglio i requisiti di business dell'applicazione per cui lo si sta realizzando. Per lavoro, il 90% del mio tempo lo passo a fare integrazioni con sistemi legacy, molte di queste vengono pianificate quando l'applicazione è già in piedi e funzionante, ed il cliente "capisce" che "integrando un pezzo con quell'altro sistema risparmia XXX click ogni volta che ha bisogno di fare ZZZ".
L'integrazione poi può avvenire direttamente sul db oppure a livello di codice. La soluzione da te proposta (vista su db) è sicuramente la più semplice ed efficace, resta però fattibile solo a patto che i due DB si "vedano" e la loro architettura E-R possa essere fatta "matchare" in qualche modo (e purtroppo questo non sempre è fattibile). Nel caso in cui l'integrazione debba avvenire direttamente a livello di codice (capita spesso se, ad esempio, l'integrazione tra i due mondi può avvenire soltanto tramite SDK proprietari), 2 parole ti salvano la vita: Adapter ed IoC.
Ovviamente l'implementazione concreta della soluzione dipende dalla specifica architettura del tuo sistema, e dal livello di complessità dell'applicazione stessa... io tipicamente costruisco un DAL specifico per gli oggetti del sistema legacy, con la sua UnitOfWork, i suoi repository, ecc, ecc, dopodichè tramite IoC rimpiazzo il worker service che orchestra le chiamate ai diversi repository (nel tuo caso, sarà il servizio che recupera ed organizza i dati da mostrare nella rubrica) con uno in grado di effettuare il dispatch delle query ai due distinti DAL. Una volta ottenuti i risultati dal sistema legacy, utilizzo un Adapter per renderne omogenea la struttura con quella dei dati caricati dal tuo db.
(Si può fare ancora meglio, prevedendo degli extensibility point nel worker service che ti consentano di iniettare a runtime la logica di fetch delle entità da fonti eterogenee, tipo plugin... ma probabilmente nel tuo caso non ne vale la pena)
Ciao,neronotte
La situazione migliore potrebbe essere quella di mettere un servizio wcf sul db legacy, e poi dalla tua applicazione chiami il servizio. Andare diretti sul db è la soluzione più veloce ma quella che poi crea il massimo accoppiamento. Meglio disaccoppiare con un servizio in modo che la tua applicazione principale sia meno legata al db legacy.
alk.
--Blog Eng: http://www.codewrecks.com/blogBlog Ita: http://blogs.ugidotnet.org/rgmTwitter: http://twitter.com/alkampfer
Ciao neronotte,
neronotte: Nel caso in cui l'integrazione debba avvenire direttamente a livello di codice (capita spesso se, ad esempio, l'integrazione tra i due mondi può avvenire soltanto tramite SDK proprietari), 2 parole ti salvano la vita: Adapter ed IoC.
Nel caso in cui l'integrazione debba avvenire direttamente a livello di codice (capita spesso se, ad esempio, l'integrazione tra i due mondi può avvenire soltanto tramite SDK proprietari), 2 parole ti salvano la vita: Adapter ed IoC.
neronotte: Ovviamente l'implementazione concreta della soluzione dipende dalla specifica architettura del tuo sistema, e dal livello di complessità dell'applicazione stessa... io tipicamente costruisco un DAL specifico per gli oggetti del sistema legacy, con la sua UnitOfWork, i suoi repository, ecc, ecc, dopodichè tramite IoC rimpiazzo il worker service che orchestra le chiamate ai diversi repository (nel tuo caso, sarà il servizio che recupera ed organizza i dati da mostrare nella rubrica) con uno in grado di effettuare il dispatch delle query ai due distinti DAL. Una volta ottenuti i risultati dal sistema legacy, utilizzo un Adapter per renderne omogenea la struttura con quella dei dati caricati dal tuo db.
Quindi, se non ho capito male, il mio servizio legge i dati un po' dal repository che mappa la sessione, un po' da qualche altra parte (sempre tramite repository), li impacchetta e li spara dove vuole lui... right? Ma manda in giro le entità vere e proprie o dei Dto?
neronotte: (Si può fare ancora meglio, prevedendo degli extensibility point nel worker service che ti consentano di iniettare a runtime la logica di fetch delle entità da fonti eterogenee, tipo plugin... ma probabilmente nel tuo caso non ne vale la pena)
...probabilmente si! ;-)
Spiega meglio, va'... ;-)
Ciao Giulio,scusa per il ritardo nella risposta, in questo periodo sono un po' busy...
petrux:Ora, devo integrare un AlternativeContact proveniente da una vista sul db del cliente (che tra le altre cose ha un Id di tipo *diverso* da quello di Contact). Se uso un servizio esterno, non perdo la possibilità di "integrare" i due contatti in maniera trasparente (i.e. polimorfismo) in PhoneBook, usando semplicemente IContact? In uno scenario del genere, come ti muoveresti? Ho provato con il mapping polimorfico, ma col fatto degli Id differenti è stato un dramma... :-(
dipende da cosa intendi con "trasparente". Supponi che i dati contenuti nella vista del cliente ti vengano forniti tramite un servizio WCF (oppure li recuperi utilizzando un secondo ObjectContext che punta al DB legacy): puoi comunque fare in modo che l'entità "AlternativeContact" restituita dal servizio/estratta dal LegacyDataContext) implementi l'interfaccia IContact...
Il tuo ipotetico ContactService dovrà:
Per quanto riguarda il problema degli Id differenti, è effettivamente un problema: se i due tipi di contatti devono implementare la medesima interfaccia, l'ID deve essere dello stesso tipo. La prima soluzione che mi viene in mente per farsi "meno male possibile" è quella di astrarre il concetto di Key: definisci un'interfaccia di tipo IKey, e diversi "tipi" di chiavi concrete (KeyInt, KeyString, KeyGuid, ecc). Ogni implementazione di IKey dovrà implementare in modo opportuno gli operatori di uguaglianza (necessari per i confronti tra diversi Contact)... l'interfaccia IContact esporrà un Id di tipo IKey, che nel Contact"standard" sarà in realtà di tipo KeyInt, mentre nell'AlternativeContact sarà di tipo KeyString.
petrux:Quindi, se non ho capito male, il mio servizio legge i dati un po' dal repository che mappa la sessione, un po' da qualche altra parte (sempre tramite repository), li impacchetta e li spara dove vuole lui... right? Ma manda in giro le entità vere e proprie o dei Dto?
capito benissimo :) puoi tranquillamente mandare in giro le entità vere e proprie, ed utilizzare i Dto solo se e quando dovessero servire veramente (es, nei filtri sulla vista - i presume).
neronotte: Ciao Giulio,scusa per il ritardo nella risposta, in questo periodo sono un po' busy...
Ma quale scusa? Anzi, grazie a te per le risposte.
neronotte: petrux:Ora, devo integrare un AlternativeContact proveniente da una vista sul db del cliente (che tra le altre cose ha un Id di tipo *diverso* da quello di Contact). Se uso un servizio esterno, non perdo la possibilità di "integrare" i due contatti in maniera trasparente (i.e. polimorfismo) in PhoneBook, usando semplicemente IContact? In uno scenario del genere, come ti muoveresti? Ho provato con il mapping polimorfico, ma col fatto degli Id differenti è stato un dramma... :-( dipende da cosa intendi con "trasparente". Supponi che i dati contenuti nella vista del cliente ti vengano forniti tramite un servizio WCF (oppure li recuperi utilizzando un secondo ObjectContext che punta al DB legacy): puoi comunque fare in modo che l'entità "AlternativeContact" restituita dal servizio/estratta dal LegacyDataContext) implementi l'interfaccia IContact... Il tuo ipotetico ContactService dovrà: Creare un PhoneBook Caricare i contatti dal tuo db ed inserirli nel PhoneBook Caricare i contatti dal sistema legacy ed inserirli nel PhoneBook
Chiarissimo. Quello che intendo per "trasparente" è questo: a livello di object-model ho una classe PhoneBook fatta più o meno così:
class PhoneBook {
//un po' di properties...
AddContact(IContact c) {...}
RemoveContact(IContact c) { ... }
private IList<IContact> contactList;
IEnumerable<IContact> ContactList { get; }
}
in cui il campo private contactList è mappato sulla relazione PhoneBook(1)<-->(*)Contact (per la generazione del Dal uso NHibernate). Vorrei che chi comsumasse i servizi, possa tranquillamente usare un PhoneBook senza starsi a preoccupare di nulla.
neronotte: Per quanto riguarda il problema degli Id differenti, è effettivamente un problema: se i due tipi di contatti devono implementare la medesima interfaccia, l'ID deve essere dello stesso tipo. La prima soluzione che mi viene in mente per farsi "meno male possibile" è quella di astrarre il concetto di Key: definisci un'interfaccia di tipo IKey, e diversi "tipi" di chiavi concrete (KeyInt, KeyString, KeyGuid, ecc). Ogni implementazione di IKey dovrà implementare in modo opportuno gli operatori di uguaglianza (necessari per i confronti tra diversi Contact)... l'interfaccia IContact esporrà un Id di tipo IKey, che nel Contact"standard" sarà in realtà di tipo KeyInt, mentre nell'AlternativeContact sarà di tipo KeyString.
Ottimo suggerimento. Grazie. :-)
neronotte: petrux:Quindi, se non ho capito male, il mio servizio legge i dati un po' dal repository che mappa la sessione, un po' da qualche altra parte (sempre tramite repository), li impacchetta e li spara dove vuole lui... right? Ma manda in giro le entità vere e proprie o dei Dto? capito benissimo :) puoi tranquillamente mandare in giro le entità vere e proprie, ed utilizzare i Dto solo se e quando dovessero servire veramente (es, nei filtri sulla vista - i presume).
Perfetto.
Grazie delle dritte e buona giornata,