Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Suggerimento
Questo contenuto è un estratto dell'eBook, Architettura di microservizi .NET per applicazioni .NET containerizzati, disponibile in documentazione .NET o come PDF scaricabile gratuitamente leggibile offline.
La cartella della soluzione contiene una cartella SeedWork . Questa cartella contiene classi di base personalizzate che è possibile usare come base per le entità di dominio e gli oggetti valore. Usare queste classi di base in modo da non avere codice ridondante nella classe oggetto di ogni dominio. La cartella per questi tipi di classi è denominata SeedWork e non è simile a Framework. Si chiama SeedWork perché la cartella contiene solo un piccolo subset di classi riutilizzabili che non possono essere effettivamente considerate un framework. Seedwork è un termine introdotto da Michael Feathers e diffuso da Martin Fowler, ma è anche possibile denominare la cartella Common, SharedKernel o simile.
La figura 7-12 mostra le classi che formano il seedwork del modello di dominio nel microservizio di ordinamento. Include alcune classi di base personalizzate, ad esempio Entity
, ValueObject
e Enumeration
, e alcune interfacce. Queste interfacce (IRepository
e IUnitOfWork
) informano il livello dell'infrastruttura su ciò che deve essere implementato. Queste interfacce vengono usate anche tramite iniezione delle dipendenze dallo strato applicativo.
Contenuto dettagliato della cartella SeedWork, contenente classi e interfacce di base: Entity.cs, Enumeration.cs, IAggregateRoot.cs, IRepository.cs, IUnitOfWork.cs e ValueObject.cs.
Figura 7-12. Un set di esempi di classi e interfacce di base del modello di dominio "seedwork"
Questo è il tipo di copia e incolla riutilizzo che molti sviluppatori condividono tra progetti, non un framework formale. È possibile avere seedworks in qualsiasi livello o libreria. Tuttavia, se il set di classi e interfacce diventa sufficientemente grande, è possibile creare una singola libreria di classi.
Classe di base personalizzata di entità
Il codice seguente è un esempio di una classe base Entity in cui è possibile inserire codice che può essere usato allo stesso modo da qualsiasi entità di dominio, ad esempio l'ID entità, gli operatori di uguaglianza, un elenco di eventi di dominio per entità e così via.
// COMPATIBLE WITH ENTITY FRAMEWORK CORE (1.1 and later)
public abstract class Entity
{
int? _requestedHashCode;
int _Id;
private List<INotification> _domainEvents;
public virtual int Id
{
get
{
return _Id;
}
protected set
{
_Id = value;
}
}
public List<INotification> DomainEvents => _domainEvents;
public void AddDomainEvent(INotification eventItem)
{
_domainEvents = _domainEvents ?? new List<INotification>();
_domainEvents.Add(eventItem);
}
public void RemoveDomainEvent(INotification eventItem)
{
if (_domainEvents is null) return;
_domainEvents.Remove(eventItem);
}
public bool IsTransient()
{
return this.Id == default(Int32);
}
public override bool Equals(object obj)
{
if (obj == null || !(obj is Entity))
return false;
if (Object.ReferenceEquals(this, obj))
return true;
if (this.GetType() != obj.GetType())
return false;
Entity item = (Entity)obj;
if (item.IsTransient() || this.IsTransient())
return false;
else
return item.Id == this.Id;
}
public override int GetHashCode()
{
if (!IsTransient())
{
if (!_requestedHashCode.HasValue)
_requestedHashCode = this.Id.GetHashCode() ^ 31;
// XOR for random distribution. See:
// https://learn.microsoft.com/archive/blogs/ericlippert/guidelines-and-rules-for-gethashcode
return _requestedHashCode.Value;
}
else
return base.GetHashCode();
}
public static bool operator ==(Entity left, Entity right)
{
if (Object.Equals(left, null))
return (Object.Equals(right, null));
else
return left.Equals(right);
}
public static bool operator !=(Entity left, Entity right)
{
return !(left == right);
}
}
Il codice precedente che usa un elenco di eventi di dominio per entità verrà illustrato nelle sezioni successive quando ci si concentra sugli eventi di dominio.
Contratti di repository (interfacce) nel livello del modello di dominio
I contratti repository sono semplicemente interfacce .NET che esprimono i requisiti del contratto dei repository da usare per ogni aggregazione.
I repository stessi, con codice EF Core o qualsiasi altra dipendenza dell'infrastruttura e codice (Linq, SQL e così via), non devono essere implementati all'interno del modello di dominio; I repository devono implementare solo le interfacce definite nel modello di dominio.
Un modello correlato a questa procedura (inserendo le interfacce del repository nel livello del modello di dominio) è il modello di interfaccia separata. Come spiegato da Martin Fowler, "Utilizzare l'Interfaccia Separata per definire un'interfaccia in un pacchetto ma implementarla in un altro. In questo modo un client che necessita della dipendenza all'interfaccia può essere completamente inconsapevole dell'implementazione."
Seguendo il modello di interfaccia separata, il livello applicazione (in questo caso, il progetto API Web per il microservizio) deve avere una dipendenza dai requisiti definiti nel modello di dominio, ma non una dipendenza diretta al livello di infrastruttura/persistenza. Inoltre, è possibile usare l'iniezione di dipendenze per isolare l'implementazione, che viene effettuata nel livello di infrastruttura/persistenza utilizzando i repository.
Ad esempio, l'esempio seguente con l'interfaccia IOrderRepository definisce le operazioni che la classe OrderRepository dovrà implementare a livello di infrastruttura. Nell'implementazione corrente dell'applicazione, il codice deve solo aggiungere o aggiornare ordini al database, poiché le query vengono suddivise seguendo l'approccio CQRS semplificato.
// Defined at IOrderRepository.cs
public interface IOrderRepository : IRepository<Order>
{
Order Add(Order order);
void Update(Order order);
Task<Order> GetAsync(int orderId);
}
// Defined at IRepository.cs (Part of the Domain Seedwork)
public interface IRepository<T> where T : IAggregateRoot
{
IUnitOfWork UnitOfWork { get; }
}
Risorse aggiuntive
- Martin Fowler. Interfaccia separata.
https://www.martinfowler.com/eaaCatalog/separatedInterface.html