Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Tipp
Dieser Inhalt ist ein Auszug aus dem eBook .NET Microservices Architecture for Containerized .NET Applications, verfügbar auf .NET Docs oder als kostenlose herunterladbare PDF, die offline gelesen werden kann.
Der Lösungsordner enthält einen SeedWork-Ordner . Dieser Ordner enthält benutzerdefinierte Basisklassen, die Sie als Basis für Ihre Domänenentitäten und Wertobjekte verwenden können. Verwenden Sie diese Basisklassen, damit Sie keinen redundanten Code in der Objektklasse jeder Domäne haben. Der Ordner für diese Klassentypen wird als SeedWork und nicht als Framework bezeichnet. Es wird SeedWork genannt, da der Ordner nur eine kleine Teilmenge wiederverwendbarer Klassen enthält, die nicht wirklich als Framework betrachtet werden können. Seedwork ist ein Begriff, der von Michael Feathers eingeführt und von Martin Fowler populär wird, aber Sie könnten auch diesen Ordner "Common", "SharedKernel" oder "Ähnlich" nennen.
Abbildung 7-12 zeigt die Klassen, die den Seedwork des Domänenmodells im Sortier-Microservice bilden. Es enthält einige benutzerdefinierte Basisklassen wie Entity, ValueObject und Enumeration sowie einige Schnittstellen. Diese Schnittstellen (IRepository und IUnitOfWork) informieren die Infrastrukturschicht darüber, was implementiert werden muss. Diese Schnittstellen werden auch über die Abhängigkeitsinjektion von der Anwendungsebene verwendet.
Der detaillierte Inhalt des SeedWork-Ordners mit Basisklassen und Schnittstellen: Entity.cs, Enumeration.cs, IAggregateRoot.cs, IRepository.cs, IUnitOfWork.cs und ValueObject.cs.
Abbildung 7-12. Ein Beispielsatz von Basisklassen und Schnittstellen für das Domänenmodell "Seedwork"
Dies ist die Art der Wiederverwendung von Kopieren und Einfügen, die viele Entwickler zwischen Projekten teilen, nicht ein formales Framework. SeedWork-Ordner können in jeder Ebene oder Bibliothek vorkommen. Wenn der Satz von Klassen und Schnittstellen jedoch groß genug wird, möchten Sie möglicherweise eine einzelne Klassenbibliothek erstellen.
Die benutzerdefinierte Entitätsbasisklasse
Der folgende Code ist ein Beispiel für eine Entity-Basisklasse, in der Sie Code platzieren können, der von jeder Domänenentität wie der Entitäts-ID, Gleichheitsoperatoren, einer Domänenereignisliste pro Entität usw. auf die gleiche Weise verwendet werden kann.
// 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://v4.hkg1.meaqua.org/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);
}
}
Der vorherige Code, der eine Domänenereignisliste pro Entität verwendet, wird in den nächsten Abschnitten erläutert, wenn er sich auf Domänenereignisse konzentriert.
Repositorykontrakte (Schnittstellen) in der Domänenmodell-Schicht
Repository-Verträge sind einfach .NET-Schnittstellen, die die Vertragsanforderungen der Repositorys zum Ausdruck bringen, die für jedes Aggregat verwendet werden sollen.
Die Repositorys selbst mit EF Core-Code oder anderen Infrastrukturabhängigkeiten und -code (Linq, SQL usw.) dürfen nicht innerhalb des Domänenmodells implementiert werden; die Repositorys sollten nur die Schnittstellen implementieren, die Sie im Domänenmodell definieren.
Ein ähnliches Prinzip, bei dem die Repositoryschnittstellen in der Ebene des Domänenmodells platziert werden, ist das Schnittstellenaufteilungsprinzip. Wie von Martin Fowler erläutert , "Verwenden Sie getrennte Schnittstelle, um eine Schnittstelle in einem Paket zu definieren, implementieren Sie sie aber in einem anderen. Auf diese Weise kann ein Client, der die Abhängigkeit zur Schnittstelle benötigt, völlig nicht über die Implementierung wissen."
Nach dem Muster "Getrennte Schnittstelle" kann die Anwendungsschicht (in diesem Fall das Web-API-Projekt für den Microservice) eine Abhängigkeit von den anforderungen haben, die im Domänenmodell definiert sind, jedoch keine direkte Abhängigkeit von der Infrastruktur-/Persistenzschicht. Darüber hinaus können Sie Dependency Injection verwenden, um die Implementierung zu isolieren, die in der Infrastruktur-/Persistenzebene mithilfe von Repositorys implementiert wird.
Im folgenden Beispiel mit der IOrderRepository-Schnittstelle wird beispielsweise definiert, welche Vorgänge die OrderRepository-Klasse auf der Infrastrukturebene implementieren muss. Bei der aktuellen Implementierung der Anwendung muss der Code der Datenbank lediglich Bestellungen hinzufügen oder aktualisieren, da Abfragen nach dem vereinfachten CQRS-Ansatz aufgeteilt werden.
// 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; }
}
Weitere Ressourcen
- Martin Fowler. Getrennte Schnittstelle.
https://www.martinfowler.com/eaaCatalog/separatedInterface.html