Ho cercato di refactoring un vecchio design in un Onion Architecture in stile DDD. A quel punto, il progetto è passato da un monolite a 4 sottoprogetti:
/src
/Project.Application
/Services
/Project.Core
/Domain
/RepositoryInterfaces
/Project.Infrastructure
/DataModels
/DataServiceInterfaceAndImplementations
/RepositoryImplementations
/Project.Web
/Controllers
/ViewModels
/Views
/test
/Project.Core.Tests
/Project.Infrastructure.Tests
/Project.Web.Tests
Core
Il core contiene i miei oggetti di dominio, il cui nome deriva dagli oggetti concettuali reali nel sistema, così come l'utente aziendale potrebbe capirlo.
class SaleType {
string Name { get; set; }
}
Core contiene anche le interfacce del mio repository, che prendono il nome dal modello di dominio che il repository dovrebbe restituire.
interface ISaleTypeRepository {
SaleType GetSaleTypeForADepartmentSale(int departmentSaleId);
IList<SaleType> GetAllowedSaleTypesForADepartment(int departmentId);
}
Infrastructure
L'infrastruttura contiene i servizi dati e le implementazioni specifici del nostro back end AS400.
Esistono Datamodels per facilitare la comunicazione tra il dominio e il database.
class SaleTypeModel
{
public int Id { get; set; }
public string Name { get; set; }
}
E le interfacce sono il modo in cui restituiamo i datamodels dal livello del database:
interface ISaleTypeDataService
{
SaleTypeModel GetSaleType(int departmentSaleId);
}
interface IDepartmentAllowableSaleTypesDataService
{
DepartmentAllowableSaleTypeModel GetDepartmentAllowableSaleType(int departmentAllowedSaleId);
}
La cartella repositoryimplementations contiene quindi le implementazioni dei repository dal core, la delega ai servizi dati nell'infrastruttura per ottenere i dati e la mappatura all'oggetto dominio appropriato (in questo caso SaleType
da precedente). Sebbene i miei dataservices nel livello Infrastructure possano restituire diversi tipi di datamodel che rappresentano un aspetto di un tipo di vendita - ad esempio un "DepartmentAllowedSaleTypeModel", il repository delegherà al servizio dati quindi li mapperà internamente all'oggetto dominio SaleType.
class SaleTypeRepository : ISaleTypeRepository
{
private readonly ISaleTypeDataService _saleTypeDataService;
private readonly IDepartmentAllowableSaleTypesDataService _departmentAllowableSaleTypesDataService;
public SaleTypeRepository(ISaleTypeDataService saleTypeDataService, IDepartmentAllowableSaleTypesDataService departmentAllowableSaleTypesDataService)
{
_saleTypeDataService = saleTypeDataService;
_departmentAllowableSaleTypesDataService = departmentAllowableSaleTypesDataService;
}
public IList<SaleType> GetAllowedSaleTypesForADepartment(int departmentId)
{
var departmentAllowedSaleTypes =
_departmentAllowableSaleTypesDataService.GetAllowableSaleTypesForDepartment(departmentId);
return departmentAllowedSaleTypes.Select(model => new SaleType { Name = model.SaleTypeName}).ToList();
}
public SaleType GetSaleTypeForADepartmentSale(int departmentSaleId)
{
var saleTypeForDepartmentSale = _saleTypeDataService.GetSaleType(departmentSaleId);
return new SaleType {Name = saleTypeForDepartmentSale.Name};
}
}
Web
Il Web è dove tutto si unisce come un normale progetto MVC. I controller fanno riferimento a IRepository dal core, l'integrazione della dipendenza si collega alle implementazioni del repository nell'infrastruttura e apre un sito web che rimbalza.
Questo mi sembra abbastanza semplice: il concetto di provenienza dei dati si trova sul bordo esterno della cipolla nell'infrastruttura. Le interfacce con i dati risiedono accanto ai servizi dati nella cartella dei servizi dati mentre il repository principale e le informazioni sul dominio sono presenti nel progetto principale.
Ritengo che questo mi dia anche un buon modo per affrontare le implementazioni dei pattern di UnitOfWork come Entity Framework e NHibernate in futuro dato che queste librerie possono stare bene in infrastruttura, mappare i modelli di dominio e tutti sono felici.
Per quanto riguarda la mia comprensione dell'architettura della cipolla, le "dipendenze" dovrebbero essere portate agli strati più lontani. Ma poi abbiamo situazioni strane come in EF, dove genera un elenco di modelli di database che devono poi essere mappati al dominio, quindi quei modelli di dominio devono essere nuovamente mappati al livello viewmodel nel livello dell'interfaccia utente. Mi è sembrato sbagliato, quindi sto cercando feedback sul fatto che io sia o meno in linea con i principi dell'architettura.
Il progetto monolite non funziona per me e per i miei colleghi e ora sto studiando architetture alternative.