La mia critica principale del tuo approccio sarebbe, non rendere il tuo BL direttamente dipendente dal tuo DAL, metti uno strato di riferimento indiretto nel loro dove il BL riceve il DAL tramite IOC o IPC (che è fondamentalmente una forma di IOC) Quindi i consumatori del tuo BL potranno scegliere cosa otterrà il DAL; Se il consumatore fa un test unitario dà un DAL finto, se il consumatore è un cliente fastidioso per qualche ragione usa un CSV DAL che odi mantenere, ma per la maggior parte dei consumatori al BL viene assegnato un DAL di grandi dimensioni per uso aziendale e circa il 20% dei consumatori implementati sono il livello di presentazione delle versioni mobili che dà al BL quel DAL in memoria.
Sono un fan personale di avere un blocco condiviso di modelli di dati, 2 BL, un DAL e un livello di presentazione nel pattern MVC.
Perché 2 BL? C'è sempre un corpo di logica che vive al livello DAL per tradurre in realtà i dati richiesti in un modello di dati, e ancora più complessità al contrario, soprattutto se si ha una qualche forma di memorizzazione nella cache. Di solito le persone incorporano tutto questo nel loro DAL ma finiscono per pagare una penalità di mantenimento che non avrebbero dovuto se facessero uno strato separato sopra il DAL che riceve i componenti DAL in CIO e completa la logica.
L'altro BL dovrebbe, a mio parere, essere idempotente in un paradigma funzionale con effetti collaterali minimi o eventuali, gli effetti collaterali sono ciò che il dal è per.
Quindi il grafico delle dipendenze sarebbe (layer: cose da cui dipende):
DataModels: nothing
DAL: nothing
DALInterfaces: nothing
DALBL: DALInterfaces, DataModels
DALBLInterfaces: DALInterfaces, DataModels
BL: DataModels
BLInterfaces: DataModels
Presentation: DataModels, *Interfaces, some IOC framework or service client framework that turns the interfaces into concrete implementations.
Quindi l'uso è tale che nella tua presentazione faresti cose come:
public MenuOption[] GetMenuOptions(string username, string password)
{
DataModels.User currentUser = _securityBl.GetUserFromThread(Thread.CurrentThread);
// remember no side effects in BL layer, just works from inputs, so needs be told the thread
return _usersDalBl.GetMenuOptionsForUser(currentUser);
// this dalBl was given a concrete Dal at construction, again
// no *direct* side effects, only the dal it's accessing has the code with side effects
}
Nota, questo rende il più alto livello (che dovrebbe essere il più sottile e privo di sostanza) il più altamente dipendente, tuttavia penso che questo abbia senso, il suo scopo principale è quello di legare insieme qualsiasi elemento di funzionalità di cui ha bisogno, mentre tutte le funzionalità si trovano in realtà nelle librerie sottostanti.
I pezzi dell'interfaccia esistono solo per il test delle unità tanto quanto qualsiasi cosa, anche se la modularità può sempre essere necessaria in seguito. La mia preferenza è che IPC sia usato per accedere almeno a BL, così come a DAL, anche se dovrebbe essere un'interfaccia IPC più sicura. La forma di IPC è irrilevante, potrebbe essere solo un file mappato in memoria o altro. Penso solo che sia un limite importante mantenere l'indipendenza per quel tempo dopo, quando qualcuno scrive un DAL migliore del tuo e tutto ciò che devi fare è eseguire il loro processo DAL invece del tuo. Anche se forse sono solo un toccasana per IPC, è quasi necessario se sai che sarai in grado di mantenere i confini tra gli strati senza bisogno di protezioni per salvaguardare da incidenti / errori / altre persone.