Progettazione di un'applicazione di servizio modulare


11

Sto cercando di progettare una nuova soluzione che è molto modulare per natura e vorrei creare una struttura che supporti tale progetto per consentire una facile espansione futura, una chiara separazione delle preoccupazioni, licenze per modulo, ecc. La maggior parte di ciò che ho trovato sul web su applicazioni modulari o composite sono incentrati sull'interfaccia utente, focalizzato su Silverlight, WPF, ecc. Nel mio caso, sto sviluppando un'applicazione di servizio WCF che verrà utilizzata da altri sviluppatori che lavorano su vari progetti di interfaccia utente.

sfondo

L'obiettivo del mio progetto è quello di creare una fonte centralizzata di logica aziendale / di dominio per supportare molte delle nostre applicazioni aziendali che attualmente duplicano processi, regole, ecc. E mentre non tutti i vantaggi della modularità saranno raggiunti, vorrei cogliere il opportunità di stabilire un quadro che sfrutterà quelle parti di cui possiamo trarre vantaggio.

Ho iniziato la progettazione guardando l'API esposta dall'applicazione di servizio. È chiaro che i servizi possono essere separati secondo le linee modulari che stavo prendendo in considerazione. Ad esempio, avrò servizi FinanceService, InventoryService, PersonnelService, ecc. Che raggruppano le mie operazioni di servizio per fornire un'elevata coesione nell'API mantenendo basso l'accoppiamento, in modo che i clienti debbano solo consumare i servizi pertinenti alla loro applicazione.

È logico per me poter disporre di moduli separati per ciascuno di questi servizi, come MyApp.Finance, MyApp.Inventory, My.Personnel e così via. Preoccupazioni trasversali e tipi condivisi sarebbero nell'assemblea condivisa MyApp. Da qui mi lego un po '.

(Oh, dovrei menzionare che userò un contenitore IoC per Dependency Injection per mantenere l'applicazione liberamente accoppiata. Non menzionerò quale perché non voglio aprire la confezione di Pandora!)

In MyApp.ServiceHost, creerò un file host di servizio (.svc) corrispondente a ciascun modulo, ad esempio FinanceService.svc. L'host del servizio richiede il nome del servizio che corrisponde alle informazioni nel file di configurazione che contiene l'interfaccia che definisce il contratto di servizio. La configurazione IoC viene quindi utilizzata per mappare l'implementazione concreta dell'interfaccia da utilizzare.

1. Il livello di servizio dovrebbe implementare l'API e delegare i moduli o i moduli dovrebbero essere autonomi (in quanto contengono tutto ciò che è relativo a quel modulo, inclusa l'implementazione del servizio)?

Un modo per affrontare il problema è disporre di un "modulo" MyApp.Services che contenga l'implementazione dei contratti di servizio. Ogni classe di servizio semplicemente delegare a un'altra classe nel modulo appropriato che contiene la logica di dominio per l'operazione. Ad esempio, la classe WCF FinanceService in MyApp.Services viene delegata a un'altra interfaccia implementata nel modulo Finance per eseguire l'operazione. Ciò mi consentirebbe di mantenere una facciata di servizio sottile e di "plug-in" l'implementazione per l'implementazione del servizio, eliminando ad esempio la necessità che i moduli si preoccupino della WCF.

D'altra parte, forse è preferibile avere ogni modulo autonomo in quanto ha l'interfaccia e l'implementazione. L'host del servizio fa riferimento all'interfaccia del contratto di servizio presente nel modulo e l'IoC è configurato per utilizzare l'implementazione appropriata anche dal modulo. Ciò significa che l'aggiunta di un nuovo modulo può essere eseguita senza modifiche al livello di servizio oltre all'aggiunta di un nuovo file .svc e informazioni sulla configurazione IoC.

Sto pensando all'impatto se passo da WCF standard a un'interfaccia di servizio RESTful o forse vado ai servizi RIA o qualcosa del genere. Se ogni modulo contiene l'implementazione del contratto di servizio, allora devo modificare ogni modulo se cambio la tecnologia o l'approccio del servizio. Ma se la facciata è il suo modulo, devo solo cambiare quella parte per fare un cambiamento. I moduli dovrebbero quindi implementare una diversa serie di contratti (interfacce), eventualmente definiti nell'assembly condiviso ???

2. Qual è il modo migliore per gestire la condivisione delle risorse tra i moduli e / o le dipendenze tra i moduli?

Prendi, ad esempio, un'operazione di ricezione. A prima vista ha senso che questo entri nel modulo Inventario poiché ricevere merci è una funzione di inventario. Tuttavia, c'è anche un aspetto finanziario in quanto dobbiamo generare una ricevuta e autorizzare il pagamento.

Da un lato, mi aspetterei di utilizzare un tipo di eventi di dominio / messaggistica per comunicare l'operazione. Il modulo Inventory genera un GoodsReceivedEvent che viene gestito dal modulo finanziario per generare la ricevuta e avviare il processo di pagamento. Tuttavia, ciò significa che il modulo finanziario deve conoscere gli articoli di inventario che sono stati ricevuti. Posso semplicemente fare riferimento a loro tramite ID, ma cosa succede se ho bisogno di ulteriori informazioni per la ricevuta, come il nome e / o la descrizione, il costo unitario, ecc.? Ha senso per ogni modulo avere la propria versione di un articolo di inventario progettato per soddisfare le esigenze di quel modulo? In tal caso, il modulo finanziario dovrebbe eseguire la propria ricerca degli articoli di inventario durante la gestione di GoodsReceivedEvent.

...

Ho usato questo esempio apposta perché ho lavorato molto con vari sistemi ERP e so che sono progettati con questo tipo di modularità - non so proprio come. Inoltre, non lo menziono esplicitamente sopra, ma preferisco seguire i principi di Domain Driven Design nell'architettura della soluzione e credo che questo tipo di modularità si adatti perfettamente a quell'area.

Qualsiasi aiuto per farmi avvolgere la testa è molto apprezzato.


1
Scusate. Non riesco a trovare una domanda in tutto il pensiero. Quale è la domanda?
S.Lott

1
Cerca i punti interrogativi. Esistono diversi punti in cui offro opzioni ma non presumo una soluzione e diverse domande specifiche per guidare il processo lungo la strada giusta.
SonOfPirate,

1
@SonOfPirate: Siamo spiacenti. Speravo che potessi prenderti il ​​tempo per evidenziare o enfatizzare le domande in modo che altre persone possano aiutarti.
S. Lott,

4
Sono d'accordo con S.Lott. Mi dispiace, ma questa non è una domanda, è un flusso di coscienza. Possiamo aiutarvi molto meglio se condensate questo a una o due domande focalizzate e proviamo a separare le preoccupazioni architettoniche dai dettagli di implementazione.
Aaronaught,

1
@SonOfPirate: "Architecture" riguarda la struttura e comunica tale struttura agli altri sviluppatori. Si inizia con la descrizione. Deve raggiungere un livello di chiarezza e trasparenza che consenta ad altri sviluppatori di "ottenerlo" totalmente e seguire i principi di progettazione architettonica in tutto ciò che fanno.
S. Lott,

Risposte:


2

Ha senso per ogni modulo avere la propria versione di un articolo di inventario progettato per soddisfare le esigenze di quel modulo? In tal caso, il modulo finanziario dovrebbe eseguire la propria ricerca degli articoli di inventario durante la gestione di GoodsReceivedEvent. Dove posso tracciare il confine tra modularità e necessità di condividere informazioni?

Dovrai sempre accettare i compromessi. Questo è tutto ciò che c'è da dire alle tue domande davvero. È stata buona norma conservare le entità in un assembly separato, quindi molte applicazioni le tengono in una libreria condivisa. Ciò significa che non ci sono limiti (fisici) logici di business. Eseguendo le tue ricerche significa molto codice ridondante che implementerei solo se hai requisiti speciali in entrambi i moduli. Ovviamente, guardandolo da un punto di vista architettonico, il tuo Modulo finanziario e il tuo Modulo inventario devono comunque condividere una dipendenza. Molto probabilmente il Modulo finanziario dipende dal Modulo inventario. Se i requisiti ti consentono di far dipendere un modulo da un altro modulo, allora direi che va bene incapsulare le entità all'interno dei moduli a cui appartengono di più. Tu' Dovremo trovare un buon equilibrio tra distinzione fisica (dipendenze condivise) e manutenzione. Troppe dipendenze condivise potrebbero causare un incubo di manutenzione delle versioni. Inoltre, con un ORM ci saranno problemi se si desidera mappare nuove relazioni con entità esistenti o estenderle da moduli che dipendono dal modulo contenente l'entità.

Se non usi ORM, tieni presente che probabilmente tutti i tuoi moduli condividono lo stesso database, come spesso accade. Potresti usarlo come un terreno comune.

È inoltre possibile mantenere chiari i dati (in termini di CSV / set di risultati, ad esempio) e utilizzare un servizio di messaggistica centrale per comunicare ai moduli che vi sono stati registrati nuovi dati insieme ad alcune meta informazioni. Ciò significa che non esistono dipendenze reali, ma i sacrifici digitano safty e contratti e ci possono essere problemi con dati non validi. Immagino sia il numero di grandi sistemi ERP che lo fanno.

Quindi in breve, dovrai decidere quale tipo di interfaccia desideri. Più modularità porta a meno contratti e viceversa.

Probabilmente userò una libreria condivisa per i miei oggetti dati e un servizio di messaggistica centrale con un modello di sottoscrizione di pubblicazione, che mi sembra la soluzione migliore.


Anche d'accordo con questo - messaggistica + pub / sub e librerie condivise per contratti dto (repository interno nuget). Mi chiedevo le stesse domande come @SonOfPirate fino a questo articolo ha messo in prospettiva per me: msdn.microsoft.com/en-us/library/bb245678.aspx
diegohb
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.