Pular para o conteúdo principal

Regras de Domínio no Core

Esta página consolida princípios para manter o Core/Domain centrado na regra de negócio. Para acelerar a migração de legado, admite-se uso controlado de anotações EF básicas, sem introduzir dependências de infraestrutura no domínio.

Princípios

  • Toda regra de negócio reside no Core/Domain.
  • Exceção pragmática: permitir anotações EF básicas no domínio ([Key], [Column], [Table]) para acelerar a migração de legado.
  • Mesmo com as anotações, o Domain não deve usar DbContext nem depender de implementações de infraestrutura.
  • Entidades e Value Objects expressam invariantes; Domain Services encapsulam regras que não pertencem a uma entidade específica.

Anotações EF permitidas (exceção controlada)

  • Permitidas: [Key], [Column], [Table].
  • Continue evitando mapeamentos complexos no domínio (conversores, relacionamentos avançados, etc.). Esses ficam na Infra.

Exemplo de Entidade com Data Annotations EF

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

[Table("Orders")]
public sealed class Order : AggregateRoot
{
private readonly List<OrderLine> lines = new();

[Key]
[Column("Id")]
public Guid Id { get; private set; }

[Column("CustomerId")]
public Guid CustomerId { get; private set; }

public IReadOnlyCollection<OrderLine> Lines => lines;

private Order(Guid customerId)
{
if (customerId == Guid.Empty) throw new DomainException("Cliente inválido.");
Id = Guid.NewGuid();
CustomerId = customerId;
}

public static Order Create(Guid customerId, IEnumerable<(Guid productId, int qty, decimal price, string currency)> input)
{
var order = new Order(customerId);
foreach (var (productId, qty, price, currency) in input)
order.lines.Add(OrderLine.Create(productId, qty, Money.From(price, currency)));
return order;
}
}

public sealed class OrderLine : Entity
{
public Guid ProductId { get; private set; }
public int Quantity { get; private set; }
public Money UnitPrice { get; private set; }

private OrderLine(Guid productId, int quantity, Money unitPrice)
{
if (productId == Guid.Empty) throw new DomainException("Produto inválido.");
if (quantity <= 0) throw new DomainException("Quantidade deve ser positiva.");
ProductId = productId;
Quantity = quantity;
UnitPrice = unitPrice;
}

public static OrderLine Create(Guid productId, int quantity, Money unitPrice)
=> new(productId, quantity, unitPrice);
}

Exemplo de Value Object (sem dependência de EF)

public sealed class Money : ValueObject
{
public decimal Amount { get; }
public string Currency { get; }

private Money(decimal amount, string currency)
{
if (amount < 0) throw new DomainException("Valor negativo não permitido.");
if (string.IsNullOrWhiteSpace(currency)) throw new DomainException("Moeda é obrigatória.");
Amount = amount;
Currency = currency;
}

public static Money From(decimal amount, string currency) => new(amount, currency);

protected override IEnumerable<object?> GetEqualityComponents()
{
yield return Amount;
yield return Currency;
}
}

Dependência mínima de EF

  • Não usar DbContext no Domain.
  • Restringir-se a anotações simples para mapear tabelas/colunas.
  • Mapeamentos avançados permanecem em Infra (ex.: EntityTypeConfiguration).
  • Para mapeamento de views, ver a página Views.