ciao,
totti240282 ha scritto:
Al posto di WCF Data Service non sarebbero meglio le Web Api ?
Sì, si potrebbe usare anche una Web API. In questo caso ho suggerito un WCF Data Service perché avendo un'applicazione WPF magari voleva usare un trasporto diverso da HTTP, anche perché fino a questo momento non si è parlato di un server web. WCF puoi ospitarlo anche in un servizio per Windows o in un progetto Console, quindi in mancanza di ulteriori dettagli mi sono tenuto sulla soluzione più versatile.
AdeptusAstartes ha scritto:
Per mia grande fortuna gli utenti possono lavorare solo da pc dell'azienda e all'interno dell'azienda, un problema in meno, motivo per cui non ho considerato WCF
Bene così :)
AdeptusAstartes ha scritto:
come creo il domain layer?
Parto dal db? Parto dagli oggetti?
Parti dagli oggetti (si dovrebbe dire
classi di entità perché gli oggetti, o
entità, sono istanze di quelle classi).
AdeptusAstartes ha scritto:
ho forse capito quale è la logica di dominio
Una classe non è soltanto una collezione di proprietà ma può (e dovrebbe) possedere della logica di business, cioè metodi che ne espongano il comportamento e che, come conseguenza della loro invocazione, modificano lo stato interno dell'entità.
Faccio un esempio.
Tu dici di avere un'entità Controllo che viene avviata su entità Processo. Concentriamoci su Controllo: l'approccio più intuitivo è quello di creare nella classe una proprietà chiamata
Conclusa di tipo booleano, il cui valore predefinito è
false ma viene impostata a
true nel momento in cui l'utente decide che quel controllo è stato completato.
In realtà progettare lo status del Controllo in questo modo già vìola una delle raccomandazioni del DDD, cioè l'Ubiquitous Language. Tu non troverai mai un committente che ti dice "Quando il controllo è completo, io scrivo
true sulla scheda".
Un miglioramento che potresti fare è quello di rinominare la proprietà "Concluso" in "Status" (o qualsiasi altro termine usiate) cambiare il tipo da booleano ad un'enumerazione che preveda le voci InCorso e Completo.
EF5 supporta le enumerazioni e in questo modo consenti l'aggiunta di altre voci che potrebbero rendersi necessarie in futuro (es. InPausa, Cancellato).
Già sei più aderente al principio dell'Ubiquitous Language se il committente ti dice: "Quando il controllo è completo io scrivo 'Completo' sulla scheda".
Tuttavia quest'affermazione nasconde qualcosa di più complesso, che solitamente viene rivelato dopo alcuni incontri con il committente.
Ci saranno certamente delle situazioni in cui il Controllo non può essere completato perché non sono state svolte determinate operazioni, come fornire dei documenti d'appoggio od un'esito finale.
Alcuni progetti vanno incontro a bug e serie difficoltà se lo svilippatore pensa che la situazione sia semplice e chiara fin dal principio, ed inizia a scrivere righe di codice come questa:
controllo.Status = Status.Completo;
Questa riga di codice cambia il valore di Status in maniera permanente e ignora tutte le precondizioni necessarie affinché lo Status possa essere spostato su Completo. Questo codice è pericoloso perché riesce a completare un Controllo anche se non c'erano i requisiti per farlo.
Un modo migliore per gestire questa situazione è esporre un metodo Completa() che verrà invocato da un apposito bottone (guardati la metodologia di design chiamata
task-based UI)
Inizialmente l'implementazione del metodo nella classe Controllo sarà semplicemente questa.
public void Completa(){
this.Status = Status.Completo;
}
Che sembra una complicanza inutile, ma ti aiuterà moltissimo nel futuro, quando verrai a conoscenza di requisiti che non erano emersi nei primi incontri col committente.
Se lui ad un certo punto ti dicesse: "No, guarda, non ci eravamo capiti ma per poter completare un Controllo c'è bisogno che il tecnico abbia caricato un documento e che fornisca l'esito del controllo".
A questo punto puoi integrare il corpo del metodo Completa di tutta la nuova logica di business ed eventualmente modificarne il numero di parametri.
[Obsolete("Non più supportato, devi fornire un esito", true)]
public void Completa(){
throw new Exception("Non più supportato, devi fornire un esito");
}
public void Completa(Esito esito){
if (this.DocumentoDiAppoggio == null)
throw new ImpossibileCompletareException("Manca il documento di appoggio");
this.Status = Status.Completo;
}
In questo modo la logica è incapsulata nella classe. Se non l'avessi fatto, saresti dovuto andarti a trovare tutti i punti, nella tua applicazione, in cui si assegnava lo Status a Completato, e modificare quella logica esternamente. Capisci se fai lo sforzo di non usare i setter delle proprietà ma i metodi della classe, sarai meno esposto a problemi.
Anche per un semplice cambio password, predisponi il metodo CambiaPassword perché dovrà comunque incapsulare un minimo di logica, tipo controllare che la nuova password non sia identica a quella fornita e che raggiunga i criteri minimi di sicurezza.
Dunque, per ripendere la tua domanda:
AdeptusAstartes ha scritto:
come creo il domain layer?
Parto dal db? Parto dagli oggetti?
Parti dalle classi di entità, iniziando a modellare prima quelle il cui comportamento ti sembra sufficientemente chiaro.
Anzi, dato che vuoi impiegare il Test Driven Development scrivi prima il test e lasciallo fallire (questo è utile perché richiede che tu abbia ragionato sull'uso che andrai a fare del metodo). Solo POI implementa la logica di business nei metodi di cui abbiamo parlato. Se tutto è stato fatto correttamente, il test passerà.
AdeptusAstartes ha scritto:
Sempre nel libro ho letto delle fluent interface e mi sono piaciute ma le applico anche qui??
Dipende. L'esperto di dominio è il committente, devi ascoltare come parla e capire se un'interfaccia fluente può fare al caso tuo.
Se ti dice:
"Io, quando creo un controllo, scelgo prima il processo e poi gli assegno due persone che dovranno seguirlo. Poi scelgo una data di inizio e faccio una previsione sulla data di fine. In ultimo, imposto un numero massimo di ore al giorno da destinargli."
Allora, dato che la costruizione è un processo a più fasi, potrebbe aver senso crearsi una factory, eventualmente con interfaccia fluente.
var nuovoControllo =
servizioCreazioneControllo //questa istanza mi viene fornita dal container IoC. Programma contro interfacce, non contro implementazioni concrete
.DaProcesso(processo)
.AssegnaPersonale(impiegato1, impiegato2)
.IniziaIl(DateTime.Today)
.FinePrevistaIl(DateTime.Today.AddDays(20))
.PerUnMassimoDiOreGiornaliere(2)
.Crea();
Che tu usi l'interfaccia fluente o no, delegare la costruzione ad una factory, in questo caso, ti permette di racchiudere in essa della logica che non è di stretta pertinenza dell'entità Controllo. Ad esempio, se impiegato1 fosse già impegnato in altri progetti e non potrebbe dedicare 2 ore al giorno a quel Controllo, è la factory che dovrebbe lanciare un'eccezione.
ciao,
Moreno
Modificato da BrightSoul il 19 agosto 2013 00.18 -