Vorrei condividere la mia esperienza personale nell'esplorazione di questo esatto problema nella migrazione di un'applicazione WinForms in un'applicazione web ASP.NET Core. Vorrei notare che questa soluzione organizzava principalmente servizi di backend che fornivano servizi di integrazione / orchestrazione a un front-end.
Come accennato in Ant P, esiste una scelta tra il raggruppamento del codice in unità "verticale" (funzionale) e "orizzontale" (livello / classe). La mia preferenza personale è quella di organizzare sempre codice e spazi dei nomi in unità schierabili tramite l'approccio "verticale" anziché raggruppare tutte le "entità" in un ampio spazio di nomi Models
trasversale e tutto " dati che ottengono / elaborano le classi "in uno spazio dei nomi Services
separato secondo l'approccio" orizzontale ".
Dalla tua domanda e dai tuoi commenti, presumo che alla fine desideri esporre API per servire più siti web, probabilmente da uno spazio dei nomi MySolution.Web.Controllers
. Attualmente, tutto il mio codice è in un progetto a causa di una decisione precedente dello sviluppatore, ma organizzo tutti gli altri moduli in modo che possano essere facilmente estrapolati in un altro progetto quando necessario .
Per ottenere un approccio "verticale", ho uno spazio dei nomi Domains
che contiene agnostico dell'applicazione riguarda separato da specifiche dell'applicazione come il tuo fratello Controllers
o ViewModels
spazi dei nomi. Non so quanto sia flessibile la tua attuale struttura della soluzione , ma nel mio spazio dei nomi Domains
ci sono categorie funzionali come OrderManagement
e ShippingServices
. Ogni "categoria funzionale" può annidare ulteriori spazi dei nomi che si specializzano infine in classi di "implementatori" o fonti di terze parti. Per esempio. Domains.OrderManagement.SomeSalesChannel
conterrà tutto il codice per interagire con quel canale di vendita nel contesto della gestione degli ordini. Nel tuo caso, avresti uno spazio dei nomi Domains.ArticleObtainment
o il tuo nome migliore per questo problema.
Una volta che ognuno di questi "nodi foglia" di funzionalità viene stabilito all'interno dello spazio dei nomi Domains
, quindi in ogni foglia è possibile avere qualsiasi combinazione dei seguenti spazi dei nomi:
-
Entities
: tutti i tipi di dominio con identità persistente indipendente dal backing store (come rilevato da Eric King). Vedi Domain Driven Design . Questo potrebbe avere il tuo Article
e probabilmente ArticleParagraph
a condizione che abbiano un'identità persistente.
-
ValueObjects
: tipi non persistenti, enumerazioni, 'enumeri / involucri sicuri per tipo'
-
Utilities
: problemi di carattere funzionale che eseguono solo elaborazione e calcoli senza "presentare" alcuni backing store o dati di terze parti. Questo potrebbe avere il tuo IRentCalculator
ecc.
-
(Data)Services
: repository, "client" / endpoint e interfacce a qualche componente consumabile di un implementazione DbContext
che serve a fonte tutti gli elementi nel suo fratello Entities
namespace e nessun altro . Questo potrebbe avere il tuo RentService
o ArticleRepository
.
Fondamentalmente, ogni "nodo foglia" di funzionalità dovrebbe contenere tutto il necessario per servire quella categoria di funzionalità ed essere estratto in un progetto separato o "suite funzionale" per il riutilizzo attraverso i progetti quando necessario. Ora, se usi Visual Studio, come menziona Flater, dovrebbe assegnare automaticamente spazi dei nomi alle tue classi che corrispondono alla struttura della cartella della soluzione; è da queste cartelle di indipendenza funzionale che puoi estrarle in progetti separati a tuo piacimento.
Se desideri riutilizzare il codice o creare un'astrazione DI / IoC per più moduli da sfruttare, puoi aggiungere uno spazio dei nomi Base
all'interno di una categoria funzionale contenente interfacce o classi astratte per ognuna delle categorie di spazio dei nomi finali sopra menzionate e hanno solo fratelli e sorelle in tale spazio di nomi Base
che li utilizzano o li implementano. All'interno di questo sistema, puoi definire le tue regole implicite sul fatto che uno spazio dei nomi dovrebbe poter dipendere da un altro.
In una nota finale, per "specifiche dell'applicazione" separate dallo spazio dei nomi Domains
, ho uno spazio dei nomi DataLinks
per incapsulare le trasformazioni tra i tipi negli spazi dei nomi Domains
o ViewModels
. Quindi ho uno spazio dei nomi DataFlows
separato che può dipendere da DataLinks
per aggregare, instradare o orchestrare le transazioni tra quegli elementi nello spazio dei nomi Domains
. Infine, ho i miei controller solo in base a questi DataFlows
o DataServices
come necessario.