Domanda

Sono un grande fan di NTiers per le mie scelte di sviluppo, ovviamente non si adatta ogni scenario.

Attualmente sto lavorando su un nuovo progetto e sto cercando di avere un gioco con il mio modo di solito il lavoro, e cercando di vedere se riesco a ripulirlo. Come ho avuto un cattivo ragazzo e sono state mettendo troppo codice nel livello di presentazione.

Il mio normale struttura dei livelli di business è questo (vista di base di esso):

  • Business
    • Servizi
      • FooComponent
        • FooHelpers
        • FooWorkflows
      • BahComponent
        • BahHelpers
        • BahWorkflows
    • Utilità
      • Comune
      • ExceptionHandlers
      • Importatori
      • ecc ...

Ora, con il sopra ho un ottimo accesso a salvare direttamente un oggetto Foo e un oggetto Bah, attraverso i loro rispettivi collaboratori.

I XXXHelpers darmi accesso a salvare, modificare e caricare i rispettivi oggetti, ma dove ho messo la logica per salvare gli oggetti con gli oggetti figli.

Ad esempio:

Abbiamo i sotto oggetti (non molto buona oggetti che conosco)

  • dipendenti
  • EmployeeDetails
  • EmployeeMembership
  • EmployeeProfile

Attualmente mi avrebbe costruito questi tutti nel livello di presentazione e poi passarli ai loro aiutanti, sento che questo è sbagliato, penso che i dati devono essere trasmessi a un solo punto sopra la presentazione nel livello di business qualche posto e risolto lì.

Ma io sono in un po 'di una perdita su dove avrei messo questa logica e cosa chiamare il settore, sarebbe andare sotto Utilities come EmployeeManager o qualcosa di simile?

Che cosa faresti? e so che questo è tutto preferenza.

Una più dettagliata di layout

I flussi di lavoro contengono tutte le chiamate direttamente al DataRepository ad esempio:

public ObjectNameGetById(Guid id)
{
    return DataRepository.ObjectNameProvider.GetById(id);
}

E poi l'accesso fornitore aiutanti ai flussi di lavoro:

public ObjectName GetById(Guid id)
{
    return loadWorkflow.GetById(id);
}

Questo è quello di ridurre il codice duplicato, come si può avere una chiamata nel flusso di lavoro per getBySomeProperty e poi diverse chiamate nel Helper, che potrebbe fare altre operazioni e restituire i dati in modi diversi, un cattivo esempio potrebbe essere pubblica GetByIdAsc e GetByIdDesc

Per separa le chiamate al modello di dati utilizzando la DataRepository, significa che sarebbe possibile di scambiare il modello per un'altra istanza (che era il pensiero), ma ProviderHelper non è stato suddiviso in modo che non è intercambiabile, come è hardcode a EF purtroppo. I Dont intenzione di cambiare la tecnologia di accesso, ma in futuro ci potrebbe essere qualcosa di meglio o semplicemente qualcosa che tutti i bambini freschi sono ora utilizzando che potrei voler implementare invece.

projectName.Core

projectName.Business
    - Interfaces
        - IDeleteWorkflows.cs
        - ILoadWorkflows.cs
        - ISaveWorkflows.cs
        - IServiceHelper.cs
        - IServiceViewHelper.cs
    - Services
        - ObjectNameComponent
            - Helpers
                - ObjectNameHelper.cs
            - Workflows
                - DeleteObjectNameWorkflow.cs
                - LoadObjectNameWorkflow.cs
                - SaveObjectNameWorkflow.cs
    - Utilities
        - Common
            - SettingsManager.cs
            - JavascriptManager.cs
            - XmlHelper.cs
            - others...

        - ExceptionHandlers
            - ExceptionManager.cs
            - ExceptionManagerFactory.cs
            - ExceptionNotifier.cs


projectName.Data
    - Bases
        - ObjectNameProviderBase.cs
    - Helpers
        - ProviderHelper.cs
    - Interfaces
        - IProviderBase.cs
    - DataRepository.cs

projectName.Data.Model
    - Database.edmx

projectName.Entities (Entities that represent the DB tables are created by EF in .Data.Model, this is for others that I may need that are not related to the database)
    - Helpers
        - EnumHelper.cs

projectName.Presenation

(dipende da cosa la chiamata dell'applicazione è)

projectName.web
projectName.mvc
projectName.admin

I progetti di test

projectName.Business.Tests
projectName.Data.Test
È stato utile?

Soluzione

+1 per una domanda interessante.

Quindi, il problema che si descrive è piuttosto comune - mi piacerebbe prendere un approccio diverso - prima con i livelli logici e in secondo luogo con l'utilità e helper spazi dei nomi, che mi piacerebbe provare e fattore di fuori completamente - Te lo dico io perché in un secondo.

Ma in primo luogo, il mio approccio preferito qui è architettura abbastanza comune impresa che cercherò di evidenziare in breve, ma c'è molto di più la profondità là fuori. Si richiede alcuni cambiamenti radicali nel modo di pensare - con NHibernate o Entity Framework per consentire di interrogare il modello a oggetti direttamente e lasciare che l'affare ORM con cose come la mappatura da e verso il database e le relazioni lazy loading ecc Fare questo vi permetterà di implementare tutta la tua logica di business all'interno di un modello di dominio.

In primo luogo i livelli (o progetti nella soluzione);

YourApplication.Domain

Il modello di dominio - gli oggetti che rappresentano il vostro spazio problema. Questi sono semplici oggetti vecchi CLR con tutta la tua logica di business chiave. Questo è dove il vostro esempio oggetti avrebbero vissuto, e le loro relazioni sarebbe rappresentato come collezioni. Non c'è nulla in questo strato che si occupa di persistenza, ecc, è solo oggetti.

YourApplication.Data

classi Repository - queste sono le classi che si occupano di ottenere la radice di aggregazione (s) del modello di dominio.

Per esempio, è improbabile nelle classi di esempio che si potrebbe desiderare di guardare EmployeeDetails senza guardando anche dei dipendenti (un assunto lo so, ma si ottiene l'essenza - linee fattura è un esempio migliore, in genere si arriva a fattura linee tramite una fattura anziché caricamento indipendentemente). Come tali, le classi repository, di cui si ha una classe per radice Complessivo sarà responsabile per ottenere le entità iniziali fuori dal database utilizzando l'ORM in questione, nell'attuazione delle strategie di query (come il paging o l'ordinamento) e restituendo la radice aggregata al consumatore. Il repository consumerebbe il contesto dati attiva corrente (ISession in NHibernate) -. Come questa sessione viene creato dipende dal tipo di applicazione che si sta costruendo

YourApplication.Workflow

  • potrebbe anche essere chiamato YourApplication.Services, ma questo può essere confuso con i servizi web
  • Questo livello è tutto interrelati, operazioni atomiche complesse -., Piuttosto che avere un sacco di cose da chiamare nel vostro livello di presentazione, e quindi aumentare l'accoppiamento, si può avvolgere tali operazioni in flussi di lavoro o servizi
  • E 'possibile che si possa fare senza questo in molte applicazioni.

Altri livelli quindi dipendono dalla vostra architettura e l'applicazione che si sta implementando.

YourApplication.YourChosenPresentationTier

Se si sta utilizzando i servizi di Internet per distribuire i livelli, allora si creerebbe contratti DTO che rappresentano solo i dati che si stanno esponendo tra il dominio ei consumatori. Si potrebbe definire assemblatori che sanno come spostare i dati dentro e fuori di tali contratti dal dominio (che non avrebbe mai inviare oggetti di dominio oltre il filo!)

In questa situazione, e si sta anche creando il cliente, si consumerebbe i contratti di gestione e di dati definiti in precedenza nel vostro livello di presentazione, probabilmente il legame con i DTOS direttamente come ogni DTO dovrebbe essere vista specifica.

Se avete bisogno di distribuire i livelli, ricordando la prima regola di architetture distribuite è non distribuire, allora si dovrebbe consumare il flusso di lavoro / servizi e depositi direttamente da asp.net, MVC, WPF, WinForms, ecc

che solo le foglie in cui sono stabiliti i contesti di dati. In un'applicazione web, ogni richiesta è di solito abbastanza autosufficiente, quindi una richiesta ambito contesto è la cosa migliore. Ciò significa che il contesto e connessione è stabilita all'inizio della richiesta e disposta all'estremità. E 'banale per ottenere il vostro chosen IOC / dipendenza quadro di iniezione di componenti di configurazione per-richiesta per voi.

In un'applicazione desktop, WPF o WinForms, si avrebbe un contesto per ogni modulo. Questo assicura che le modifiche alle entità di dominio in un dialogo di modifica che l'aggiornamento del modello, ma non rendono al database (ad esempio: Cancellare è stato selezionato). Non interferiscono con altri contesti o peggio fine per essere accidentalmente persistito

Dependency Injection

Tutto quanto sopra sarebbe definito come interfacce prima, con implementazioni concrete realizzate attraverso un quadro di iniezione CIO e di dipendenza (la mia preferenza è il castello di Windsor). Questo ti permette di isolare, finti e di unit test singoli livelli in modo indipendente e in un'applicazione di grandi dimensioni, l'iniezione di dipendenza è un risparmiatore di vita!

tali spazi dei nomi

Infine, la ragione per cui avrebbe perso il namespace aiutanti è, nel modello di cui sopra, che non ne hanno bisogno, ma anche, come spazi dei nomi di utilità che danno agli sviluppatori pigri una scusa per non pensare a dove un pezzo di codice logicamente si siede. MyApp.Helpers. * E MyApp.Utility. * Significa solo che se ho qualche codice, per esempio un gestore di eccezioni che appartiene forse logicamente all'interno MyApp.Data.Repositories.Customers (forse è un arbitro per cliente non è unica eccezione), un pigro sviluppatore può semplicemente inserirlo nella MyApp.Utility.CustomerRefNotUniqueException senza avere realmente pensare.

Se hai quadro comune tipo di codice che è necessario per concludere, aggiungere un progetto MyApp.Framework e spazi dei nomi importanti. Se vi trovate ad aggiunta di un nuovo modello di legante, metterlo in MyApp.Framework.Mvc, se si tratta di funzionalità di registrazione comune, metterlo in MyApp.Framework.Logging e così via. Nella maggior parte dei casi, non ci dovrebbe essere alcun bisogno di introdurre un programma di utilità o di aiutanti dello spazio dei nomi.

Wrap up

In modo che graffia la superficie - la speranza è di qualche aiuto. Questo è come sto sviluppando software di oggi, e ho volutamente cercato di essere breve - se posso approfondire eventuali specifiche, fatemelo sapere. L'ultima cosa da dire su questo pezzo supponente è il sopra è per lo sviluppo ragionevolmente larga scala - se si sta scrivendo notepad versione 2 o una rubrica telefonica aziendale, quanto sopra è probabilmente eccessivo totale !!!

Saluti Tony

Altri suggerimenti

C'è un diagramma bello e una descrizione in questa pagina circa il layout dell'applicazione, alhtough sguardi più in basso l'articolo l'applicazione non sta divisa in strati fisici (progetto separato) - Entity Framework POCO Repository

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top