37 messaggi dal 08 agosto 2008
BrightSoul ha scritto:

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.


Ok, questo mi è chiaro, ma a questo punto dovrei usare sempre metodi al posto delle proprietà? O solo se prevedo di metterci della logica?

Per capirci, la proprietà Descrizione del mio controllo posso gestirla come proprietà, magari mettendoci una validazione per renderla obbligatoria, o è meglio usare un metodo modificaDescrizione() che richiede la stringa come parametro obbligatorio?


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).

Purtroppo devo rimanere su EF 4.1 visto che abbiamo ancora windows xp e quindi .net4

Premessa, al momento ho strutturato la mia solution in questo modo:
DL) progetto class library che dovrebbe racchiudere la definizione delle mie entità
DAL) progetto class library in cui vorrei racchiudere EF
BL) progetto class library con la logica di dominio (per ora prevedo di metterci anche workflow foundation ma forse lo metterò in un ulteriore progetto esterno)
UI) progetto MVVM


Colgo al balzo l'esempio dello status, nel mio caso devo gestire la storia degli stati, quindi ogni controllo ha una lista di status con la relativa data.
Due stati non si possono sovrapporre e la data di fine del precedente deve essere messa in automatico in base a quella di inizio del successivo.
Mi serve quindi creare la classe Status ma tutta la logica dovrei gestirla nel mio oggetto Controllo all'interno del Domainlayer senza quindi posticipare il tutto a quando andrò a creare il BL?

Altro esempio per capire un'altra cosa
Entità Processo:
L'utente deve essere in grado di creare e manutenere un elenco di processi, devo quindi prevedere nell'oggetto i metodi crea(), modifica() ed elimina() oppure, trattandosi di persistenza questi metodi devono stare nel DAL?

Altro esempio:
voglio gestire un progressivo come chiave ma gestito da me e non dal db per avere maggiore flessibilità, devo quindi trovare il max presente in tabella e aggiungere 1, ma dove metto questa logica?
Se lo metto nell'oggetto nel DL dovrei chiamare il DAL per accedere al db e leggere il valore max.
Lo metto nel DAL.

Partendo dall'alto la mia idea sarebbe:
L'interfaccia chiama la sua Facade nel BL
Dal BL smisto le chiamate verso DAL e DL ma come? Devo sapere cosa c'è da una parte (persistenza) e cosa dall'altra (logica)?

BrightSoul ha scritto:

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.


Ma la factory sarebbe unica per tutto il progetto? E in quale progetto: DL o BL?




Cambiando argomento ma sempre riguardo ad entity framework, come posso gestire tabelle provenienti da altri db?

Per questo nuovo applicativo vorrei sfruttare dati presenti in un altro db, per fare un esempio:
Quando crea un nuovo Controllo l'utente deve specificare a quale attività fa riferimento (le attività vengono usate da un applicativo esistente per la gestione del carico di lavoro), quindi vorrei controllare che questa esista, recuperare alcune sue informazioni per valorizzarle in automatico sul Controllo (vedi l'azienda di riferimento) e mostrare nell'interfaccia grafica alcune informazioni rivenenti da quella tabella.

Tramite questa strada vorrei fare anche la gestione dei permessi utenti: dall'altra parte sono già abbinate persone e attività, qui vorrei fare in modo che ogni persona possa vedere solo i controlli legati ad attività a cui sono abbinati, in questo modo mi troverei il tutto già pronto.

Con una SP sarebbe semplicissimo: metto in join le tabelle dei due db i finita li, solo che mi piacerebbe modellare questi oggetti nel DL ma non saprei poi come gestirli nella fase di mapping con il db per leggere i dati non avendo tabelle sottostanti nell'unico db che posso mappare con EF

Modificato da AdeptusAstartes il 19 agosto 2013 14.13 -
Modificato da AdeptusAstartes il 19 agosto 2013 14.54 -
37 messaggi dal 08 agosto 2008
Per le tabelle esterne mi viene in mente questa soluzione: sul db dell'applicazione creo una query che estrae paro paro la tabella esterna e mappo questa con EF.

L'unico inconveniente che ci vedo è che usando CodeFirst per creare il db mi ritroverò con una tabella che dovrò eliminare per creare successivamente una vista con lo stesso nome.

Poi sarà mia cura far si che il DAL esponga solo metodi di lettura su questo oggetto
Modificato da AdeptusAstartes il 20 agosto 2013 09.01 -
11.886 messaggi dal 09 febbraio 2002
Contributi
ciao,

AdeptusAstartes ha scritto:
la proprietà Descrizione del mio controllo posso gestirla come proprietà o è meglio usare un metodo modificaDescrizione()?

Dato che la Descrizione non è coinvolta nella logica di business ti direi di usare il setter e basta. Ad oggi avrebbe senso creare un metodo wrapper solo se intendi creare una task-based UI, cioè se mostri una specifica maschera all'utente in cui può modificare giusto titolo e descrizione. Al salvataggio invocheresti il metodo .AggiornaTesti(string titolo, string descrizione) oppure .AggiornaTesti(Testi testi) se decidessi di accorpare titolo e descrizione in un value type.

AdeptusAstartes ha scritto:

posso gestirla come proprietà

Comunque, la domanda non deve essere "posso farlo?" perché puoi indubbiamente fare tutto quello che vuoi.
Tra le abilità di uno sviluppatore non è tanto importante la sua capacità di evitare - oggi - ogni eventuale errore, perché è impossibile fare previsioni accurate sul come matureranno i requisiti di business nel futuro. Anzi, chi cerca di prevedere ogni situazione possibile spesso cade nella trappola dello YAGNI.

L'abilità invece sta nel capire e nell'avere la volontà di fare refactoring quando un problema si manifesta.
Se domani ti chiedessero: "Vorrei che la descrizione supportasse gli hashtags come Twitter. Ovvero, ogni qualvolta scrivo il simbolo # seguito da una parola, vorrei che automaticamente sia legato un tag che posso trovare dal box di ricerca."
In quel caso, un buono sviluppatore, non si lascerebbe influenzare dal fatto che, tanto ormai, ha usato il setter ma capirebbe che quella è una responsabilità dell'entità e che perciò la logica va incapsulata in un suo metodo.

AdeptusAstartes ha scritto:

DL) progetto class library che dovrebbe racchiudere la definizione delle mie entità
BL) progetto class library con la logica di dominio

Che intendi con "logica di dominio"? Parte della logica sarà nelle classi di entità e parte in servizi di dominio (es. la factory dei Controlli), che secondo me dovrebbero trovarsi anche loro nello stesso progetto.
Forse intendi l'application layer, un livello che adatta le classi di dominio secondo i casi d'uso. Puoi metterci i ViewModel e tutto ciò che riguarda i cross-cutting concerns, tipo logging e sicurezza.

AdeptusAstartes ha scritto:

DAL) progetto class library in cui vorrei racchiudere EF

Ok, metti in quel progetto tutti i servizi infrastrutturali. Non solo EF ma anche il servizio che spedisce email, che accede al filesystem, e così via.

AdeptusAstartes ha scritto:

Mi serve quindi creare la classe Status ma tutta la logica dovrei gestirla nel mio oggetto Controllo all'interno del Domainlayer senza quindi posticipare il tutto a quando andrò a creare il BL?

Sì, in Controllo hai già creato la tua lista di Status quindi non devi fare altro che aggiungere un elemento a quella lista ogni qualvolta vengono invocati i metodi Completa(), Abbandona(), Sospendi(), ecc..

Se lo storico degli status è un qualcosa di cui parleresti agli esperti di dominio (cioè te l'ha chiesto il committente di poter vedere l'esito degli stati) allora ha senso che quella logica sia nella classe di entità Controllo.

Se invece si trattasse di un requisito non funzionale (cioè logghi i cambi di stato giusto per poter ricostruire la situazione in un certo punto nel tempo) allora probabilmente è un qualcosa che dovresti mettere nell'application layer.
La classe Controllo dovrebbe limitarsi a sollevare un evento di dominio che verrebbe poi recapitato al servizio che logga i cambi di stato.

AdeptusAstartes ha scritto:

devo quindi prevedere nell'oggetto i metodi crea(), modifica() ed elimina()

No, le classi di dominio devono essere persistent-ignorant. Come hai detto tu, questi metodi appartengono al progetto dei servizi infrastrutturali.
Se decidi di usare il pattern repository, ha senso mettere l'interfaccia IRepostitory<T> dentro il progetto di dominio (ad esempio se qualche servizio di dominio dovesse aver bisogno di accedere al db), e là puoi letteralmente chiamare quei metodi Modifica(T entità) o Elimina(T entità). L'implementazione concreta di quell'interfaccia, però, andrebbe inserita nel progetto dei servizi infrastrutturali.

AdeptusAstartes ha scritto:

voglio gestire un progressivo come chiave ma gestito da me e non dal db per avere maggiore flessibilità, devo quindi trovare il max presente in tabella e aggiungere 1, ma dove metto questa logica?

In un servizio di dominio. Il servizio, come parte di un'unica transazione, accederà al database per ottenere il numero massimo ed incrementarlo di 1, poi assegnerà questo numero al Controllo e lo persisterà nel database.
Trovi un caso assimilabile al tuo a questo indirizzo:
http://gorodinski.com/blog/2012/04/14/services-in-domain-driven-design-ddd/
Lì viene posto l'esempio di un IInvoiceNumberGenerator che serve a generare il numero per una fattura.

AdeptusAstartes ha scritto:

L'interfaccia chiama la sua Facade nel BL
Dal BL smisto le chiamate verso DAL e DL ma come? Devo sapere cosa c'è da una parte (persistenza) e cosa dall'altra (logica)?

Mah, trattandosi di MVVM immagino che l'interfaccia invochi un comando nel ViewModel. Quest'ultimo avrà una dipendenza da un IRepository<Controllo> e perciò il container IoC, durante la costruzione, gli avrà iniettato un'implementazione concreta che si trova nel progetto dei servizi infrastrutturali. Il ViewModel a questo punto recupererà un'entità per mezzo di quel repository e poi invocherà un metodo sull'entità (es. Completa(Esito esto)). Infine chiamerà il metodo Modifica(entità) del repository, il quale farà in modo che l'entità venga persistita nel database.

AdeptusAstartes ha scritto:

Ma la factory sarebbe unica per tutto il progetto? E in quale progetto: DL o BL?

No, ogni factory dovrebbe avere una singola responsabilità. Quella di cui abbiamo parlato serviva a costruire le entità Controllo. Poi ci saranno altri servizi di dominio, come quello che ti genera i progressivi di cui abbiamo parlato prima.
Queste classi andrebbero inserite nel progetto di dominio.

AdeptusAstartes ha scritto:

Per le tabelle esterne mi viene in mente questa soluzione: sul db dell'applicazione creo una query che estrae paro paro la tabella esterna e mappo questa con EF.

Sì, puoi crearti una vista che selezioni i dati dall'altra tabella.
In alternativa puoi anche provare la strada dei sinonimi ma non l'ho mai fatto e non so se funziona con EF4. Dovresti provare.

AdeptusAstartes ha scritto:

L'unico inconveniente che ci vedo è che usando CodeFirst per creare il db mi ritroverò con una tabella che dovrò eliminare per creare successivamente una vista con lo stesso nome.

Puoi crearti un database initializer personalizzato. Là metterai la logica per creare le viste (o i sinonimi).

ciao,
Moreno

Enjoy learning and just keep making
37 messaggi dal 08 agosto 2008
preziosissimo.
domani in ufficio mi studio la risposta :)
37 messaggi dal 08 agosto 2008
Dunque...

Interessanti i sinonimi ma pensandoci bene non mi serviranno tutti i campo della tabella che collego ma solo i principali, quindi una vista forse è meglio.
Da quanto ho cercato mi pare di aver capito che anche per usare i sinonimi dovrei mettere mano al codice generato da EF...

Per gli eventi di dominio per ora ho inziato la strada dei fluent constructor:
- non avendo logica particolare da usare per le proprietà lascio i get e i set (per un attimo ho pensato di creare oggetti non modificabili creando i set privati ed esponendo un metodo modificaOggetto() ma per il momento ho deciso di lasciar stare)
- il costruttore senza parametri lo dichiaro private per farlo accedere solo da EF
- creo un costruttore che come un parametro vuole la mia FluentFactory, creata in modo tale da instanziare l'oggetto solo quando ho tutti i parametri necessari.

Come dici tu forse la fluent factory è un pò sovradimensionata rispetto ai bisogni del progetto ma avere costruttori con 20 parametri (di cui 5-6 obbligatori) non mi piace per niente e usare un dictionary non è molto meno complesso della factory alla fine, oltre a rendere peggiori le chiamate.

La storia degli stati di un Controllo è una cosa che l'utente vuol vedere quindi starà nel mio oggetto.

Ora mi vado a studiare per bene i servizi di dominio.

L'architettura potrei quindi rivederla in questo modo:

Test
Tutti i test, un namespace per ogni progetto testato

DomainLayer (DL)
divisa in 3 namespace
- Oggetti
- Factory
- DomainService

InfrastructureLayer (IL)
- EntityFramework (CRUD + query)
- Repository
- Reporting?

ApplicationLyer (AL)
- ViewModel (con la logica di business)
ma qui devo ancora studiare per bene, la mia idea è di esporre qui i livelli inferiori all'interfaccia.
(qui ad esempio ci metterei la logica che dato il metodo dublicaEsitoSuElencoProcessi(Esito e, IList<Processo> processi) che si occupa di creare le n istanze di esiti da inserire, controllando che i processi lo permettano e che gli esiti non esistano già, rimandando un messaggio di errore dettagliato all'interfaccia in caso di anomalia)

UserInterfaceLayer (UIL)
- presumo le view e basta

Sugli aspetti trasversali ho qualche dubbio: ad esempio nei test ho avuto bisogno di crearmi una classe di reflection (per testare le dataAnnotation sulle proprietà) e ho messo il tutto in un progetto separato visto che potrei usarlo altrove e onestamente non essendo logica di business non vorrei metterlo nel AL

Mi verrebbe voglia di creare un progetto Commons in cui mettere funzioni comuni, come reflectione e sicurezza (niente log questa volta)





Nel frattempo un altro paio di cose sul modello:
1)
I controlli possono essere fatti su due tipi di oggetti: su un processo o su una struttura.
Nel caso in cui siano su una struttura devo indicare l'elenco dei processi interessati, viceversa se scelgo un processo devo indicare le strutture.
Ho quindi pensato un modello con
"OggettoDelControllo" come entità principale da cui ereditano gli oggetti
"Struttura" e "Controllo"

Struttura ha un Ilist<Controllo>
Controllo ha un Ilist<Processo>

il riferimento circolare mi piace gran poco ma non vedo altre strade

2)
Molti oggetti hanno delle proprietà accessorie come tipologia o status che non so bene come mappare.
Non posso farli come valueObject perchè l'utente deve essere in grado di modificarne la descrizione (rimanendo lo stesso oggetto, ad esempio è capitato di dover passare da "iniziato" ad "in corso"), aggiungerne o crearne.
Per lo stesso motivo ho scartato gli Enum

In teoria dovrei crearli tutti come oggetti distinti ma essendo tra loro molto simili (alla fine sono tutti composti da Codice, Descrizione, Ordinamento, Annullato) e non mi piace scrivere le stesse cose n-volte.

Avrei pensato di creare un oggetto "OggettoServizio" (anche se il nome non mi entusiasma) con tutte le proprietà comuni (con relativi validator e il bel costruttore fluent) e far ereditare tutti gli altri oggetti da questo, anche senza aggiungere nessuna proprietà, solo per avere gli oggetti distinti (TipoControllo, Status...)

A prima vista mi sembra un'idea non male, che ne dici?

La perplessità l'ho su come mapparle sul DB:
Io vorrei avere sul db due sole tabelle: Processo e Struttura ma
- TPT (una tabella x tipo) mi creerebbe anche la tabella OggettoServizio inutile per me
- TPH (una tabella con colonna chee distingue) non mi piace per niente per andarci a fare query complesse per i report, oltre ad avere le prestazioni peggiori
- TPC (una tabella per classe concreta) è quello che vorrei usare ma mi pare di capire che abbia dei problemi prima di EF5 (che non posso usare) o sbaglio?
Modificato da AdeptusAstartes il 26 agosto 2013 09.43 -

Torna al forum | Feed RSS

ASPItalia.com non è responsabile per il contenuto dei messaggi presenti su questo servizio, non avendo nessun controllo sui messaggi postati nei propri forum, che rappresentano l'espressione del pensiero degli autori.