Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Dica
Esse conteúdo é um trecho do eBook, arquitetura de microsserviços do .NET para aplicativos .NET em contêineres, disponível em do .NET Docs ou como um PDF para download gratuito que pode ser lido offline.
A pasta da solução contém uma pasta SeedWork . Esta pasta contém classes base personalizadas que você pode usar como base para suas entidades de domínio e objetos de valor. Use essas classes base para que você não tenha código redundante na classe de objeto de cada domínio. A pasta para esses tipos de classes se chama SeedWork e não é algo como o Framework. Chama-se SeedWork porque a pasta contém apenas um pequeno subconjunto de classes reutilizáveis que não podem realmente ser consideradas uma estrutura. Seedwork é um termo introduzido por Michael Feathers e popularizado por Martin Fowler, mas você também pode nomear essa pasta Common, SharedKernel ou similar.
A figura 7-12 mostra as classes que formam o seedwork do modelo de domínio no microsserviço de ordenação. Ele tem algumas classes base personalizadas, como Entity, ValueObjecte Enumeration, além de algumas interfaces. Essas interfaces (IRepository e IUnitOfWork) informam a camada de infraestrutura sobre o que precisa ser implementado. Essas interfaces também são usadas por meio de Injeção de dependência da camada de aplicativo.
O conteúdo detalhado da pasta SeedWork, que contém classes base e interfaces: Entity.cs, Enumeration.cs, IAggregateRoot.cs, IRepository.cs, IUnitOfWork.cs e ValueObject.cs.
Figura 7-12. Um conjunto de amostras de interfaces e classes bases "seedwork" do modelo de domínio
Esse é o tipo de reutilização de cópia e colagem que muitos desenvolvedores compartilham entre projetos, não uma estrutura formal. É possível ter seedworks em qualquer camada ou biblioteca. No entanto, se o conjunto de classes e interfaces ficar grande o suficiente, talvez você queira criar uma única biblioteca de classes.
A classe base de entidade personalizada
O código a seguir é um exemplo de uma classe base de entidade em que você pode colocar código que pode ser usado da mesma maneira por qualquer entidade de domínio, como a ID da entidade, operadores de igualdade, uma lista de eventos de domínio por entidade etc.
// 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);
}
}
O código anterior usando uma lista de eventos de domínio por entidade será explicado nas próximas seções ao se concentrar em eventos de domínio.
Contratos de repositório (interfaces) na camada de modelo de domínio
Os contratos de repositório são simplesmente interfaces .NET que expressam os requisitos de contrato dos repositórios a serem usados para cada agregação.
Os repositórios em si, com código EF Core ou quaisquer outras dependências e código de infraestrutura (Linq, SQL etc.), não devem ser implementados dentro do modelo de domínio; os repositórios devem implementar apenas as interfaces definidas no modelo de domínio.
Um padrão relacionado a essa prática (colocando as interfaces do repositório na camada do modelo de domínio) é o padrão interface separada. Conforme explicado por Martin Fowler, "Use a Interface Separada para definir uma interface em um pacote, mas implementá-la em outro. Dessa forma, um cliente que precisa da dependência para a interface pode não estar totalmente ciente da implementação."
Seguir o padrão de Interface Separada permite que a camada do aplicativo (nesse caso, o projeto de API Web para o microsserviço) tenha uma dependência dos requisitos definidos no modelo de domínio, mas não uma dependência direta para a camada de infraestrutura/persistência. Além disso, você pode usar a Injeção de Dependência para isolar a implementação, que é implementada na camada de infraestrutura/persistência usando repositórios.
Por exemplo, o exemplo a seguir com a interface IOrderRepository define quais operações a classe OrderRepository precisará implementar na camada de infraestrutura. Na implementação atual do aplicativo, o código só precisa adicionar ou atualizar pedidos ao banco de dados, já que as consultas são divididas após a abordagem simplificada do CQRS.
// 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; }
}
Recursos adicionais
- Martin Fowler. Interface separada.
https://www.martinfowler.com/eaaCatalog/separatedInterface.html