Nuuvify.CommonPack.Domain
2.1.0-test.25100503
See the version list below for details.
dotnet add package Nuuvify.CommonPack.Domain --version 2.1.0-test.25100503
NuGet\Install-Package Nuuvify.CommonPack.Domain -Version 2.1.0-test.25100503
<PackageReference Include="Nuuvify.CommonPack.Domain" Version="2.1.0-test.25100503" />
<PackageVersion Include="Nuuvify.CommonPack.Domain" Version="2.1.0-test.25100503" />
<PackageReference Include="Nuuvify.CommonPack.Domain" />
paket add Nuuvify.CommonPack.Domain --version 2.1.0-test.25100503
#r "nuget: Nuuvify.CommonPack.Domain, 2.1.0-test.25100503"
#:package Nuuvify.CommonPack.Domain@2.1.0-test.25100503
#addin nuget:?package=Nuuvify.CommonPack.Domain&version=2.1.0-test.25100503&prerelease
#tool nuget:?package=Nuuvify.CommonPack.Domain&version=2.1.0-test.25100503&prerelease
Nuuvify.CommonPack.Domain
Biblioteca com implementações comuns para projetos baseados em Domain Driven Design (DDD). Fornece classes base, interfaces, Value Objects, enums e padrões essenciais para construção de domínios robustos.
📋 Índice
- Funcionalidades
- Instalação
- Dependências
- Configuração
- Uso
- Exemplos Práticos
- API Reference
- Troubleshooting
- Changelog
Funcionalidades
- ✅ Classes base para Domain com BaseDomain e AggregateRoot
- ✅ Value Objects prontos (CPF, CNPJ, Email, Endereço, etc.)
- ✅ Enums padronizados para domínios corporativos
- ✅ Integração com MediatR para CQRS e Domain Events
- ✅ Specifications Pattern para validações complexas de domínio
- ✅ Domain Events com observabilidade e versionamento
- ✅ AutoMapper integration para mapeamentos automáticos
- ✅ FluentValidator extensions com validações customizadas
- ✅ Notification Pattern herdado de Nuuvify.CommonPack.Extensions
- ✅ Localização pt-BR para mensagens de validação
- ✅ Compatibilidade .NET Standard 2.1 para máxima portabilidade
Instalação
Via Package Manager Console
Install-Package Nuuvify.CommonPack.Domain
Via .NET CLI
dotnet add package Nuuvify.CommonPack.Domain
Via PackageReference
<PackageReference Include="Nuuvify.CommonPack.Domain" Version="X.X.X" />
Dependências
NuGet Packages
Package | Version | Descrição |
---|---|---|
AutoMapper | 12.0.1 | Biblioteca para mapeamento automático entre objetos, facilitando conversões entre DTOs e entidades |
MediatR | 12.2.0 | Implementação do padrão Mediator para .NET, essencial para CQRS e Domain Events |
Project References
Project | Descrição |
---|---|
Nuuvify.CommonPack.Extensions | Biblioteca base com Notification Pattern, extensões para collections, strings, validações e outros utilitários essenciais |
Framework
- .NET Standard 2.1: Garante compatibilidade com .NET Core 3.0+, .NET 5+, .NET 6+, .NET 8+ e .NET Framework 4.7.2+
Configuração
1. Dependency Injection
// Program.cs ou Startup.cs
using Nuuvify.CommonPack.Domain;
// Registrar MediatR (necessário para Domain Events)
builder.Services.AddMediatR(cfg => cfg.RegisterServicesFromAssembly(Assembly.GetExecutingAssembly()));
// Registrar AutoMapper (necessário para BaseDomain)
builder.Services.AddAutoMapper(Assembly.GetExecutingAssembly());
// Registrar classes de domínio
builder.Services.AddScoped<MinhaClasseDeDominio>();
2. AutoMapper Profile (Exemplo)
public class DomainProfile : Profile
{
public DomainProfile()
{
CreateMap<PessoaEntity, PessoaDto>()
.ForMember(dest => dest.Documento, opt => opt.MapFrom(src => src.DocumentoPessoa.ToString()));
}
}
Uso
Classes Base
BaseDomain
Classe base para implementar lógica de domínio com suporte ao Notification Pattern:
public class PessoaService : BaseDomain
{
public PessoaService(IMediator mediator, IMapper mapper)
: base(mediator, mapper)
{
}
public async Task<PessoaDto> CriarPessoaAsync(CriarPessoaCommand command)
{
// Validar usando Notification Pattern
if (string.IsNullOrEmpty(command.Nome))
{
AddNotification("Nome", "Nome é obrigatório");
}
if (!IsValid())
{
return null;
}
// Criar entidade
var pessoa = new PessoaEntity(command.Nome, command.Documento);
// Mapear para DTO
return _mapper.Map<PessoaDto>(pessoa);
}
public override IList<NotificationR> ValidationResult()
{
return base.ValidationResult();
}
}
AggregateRoot
Classe base para entidades que são raiz de agregação:
public class PedidoEntity : AggregateRoot
{
public DateTime DataPedido { get; private set; }
public decimal ValorTotal { get; private set; }
public List<ItemPedido> Itens { get; private set; }
public PedidoEntity(DateTime dataPedido)
{
Id = Guid.NewGuid();
DataPedido = dataPedido;
Itens = new List<ItemPedido>();
}
public void AdicionarItem(string produto, decimal valor, int quantidade)
{
var item = new ItemPedido(produto, valor, quantidade);
Itens.Add(item);
RecalcularTotal();
}
private void RecalcularTotal()
{
ValorTotal = Itens.Sum(x => x.Valor * x.Quantidade);
}
}
Value Objects
CPF e CNPJ
public class PessoaEntity : DomainEntity
{
public DocumentoPessoa Documento { get; private set; }
public string Nome { get; private set; }
public PessoaEntity(string nome, string cpf, string cnpj, string tipoPessoa)
{
Nome = nome;
var cpfObj = new Cpf(cpf);
var cnpjObj = new Cnpj(cnpj);
var tipo = new TipoPessoa(tipoPessoa, DateTime.Now);
Documento = new DocumentoPessoa(cpfObj, cnpjObj, tipo);
}
public string ObterDocumentoFormatado()
{
if (Documento.TipoDaPessoa == "F")
{
var cpf = new Cpf(Documento.Cpf);
return cpf.Mascara(); // "999.999.999-99"
}
else
{
var cnpj = new Cnpj(Documento.Cnpj);
return cnpj.Mascara(); // "99.999.999/9999-99"
}
}
}
Email e Endereço
public class ContatoEntity : DomainEntity
{
public EmailPessoa Email { get; private set; }
public Endereco EnderecoResidencial { get; private set; }
public ContatoEntity(string email, string logradouro, string cidade, string uf, string cep)
{
Email = new EmailPessoa(email);
EnderecoResidencial = new Endereco(
tipoLogradouro: "Rua",
logradouro: logradouro,
codigoMunicipio: "3550308", // Código IBGE
nomeMunicipio: cidade,
uf: uf,
bairro: "Centro",
cep: cep,
numero: "123",
complemento: "Apto 101",
siglaPais: "BR"
);
}
public bool ValidarContato()
{
return Email.IsValid() && EnderecoResidencial.IsValid();
}
}
MediatR Integration
Comandos e Handlers
public class CriarPedidoCommand : ICommandR
{
public string Cliente { get; set; }
public List<ItemPedidoDto> Itens { get; set; }
public bool SaveChanges { get; set; } = true;
public bool RemoveNotificationsBeginning { get; set; } = false;
}
public class CriarPedidoHandler : IRequestHandler<CriarPedidoCommand, ICommandResultR>
{
private readonly IRepository<PedidoEntity> _repository;
public CriarPedidoHandler(IRepository<PedidoEntity> repository)
{
_repository = repository;
}
public async Task<ICommandResultR> Handle(CriarPedidoCommand request, CancellationToken cancellationToken)
{
var pedido = new PedidoEntity(DateTime.Now);
foreach (var item in request.Itens)
{
pedido.AdicionarItem(item.Produto, item.Valor, item.Quantidade);
}
await _repository.AddAsync(pedido);
if (request.SaveChanges)
{
await _repository.SaveChangesAsync();
}
return new NotificationResult { Success = true };
}
}
Specifications Pattern
public class PedidoValidoSpecification : BaseSpecification<PedidoEntity>
{
public override async Task IsSatisfactory(PedidoEntity entity)
{
if (entity.ValorTotal <= 0)
{
AddNotification("ValorTotal", "Pedido deve ter valor maior que zero");
}
if (!entity.Itens.Any())
{
AddNotification("Itens", "Pedido deve ter pelo menos um item");
}
foreach (var item in entity.Itens)
{
if (item.Quantidade <= 0)
{
AddNotification("Quantidade", $"Quantidade do item {item.Produto} deve ser maior que zero");
}
}
await Task.CompletedTask;
}
}
// Uso da Specification
public class PedidoService : BaseDomain
{
public async Task<bool> ValidarPedidoAsync(PedidoEntity pedido)
{
var spec = new PedidoValidoSpecification();
await spec.IsSatisfactory(pedido);
if (!spec.ValidationResult().Any())
{
return true;
}
AddNotifications(spec.ValidationResult());
return false;
}
}
Domain Events
public class PedidoCriadoEvent : DomainEvent<Guid>
{
public string Cliente { get; }
public decimal Valor { get; }
public int QuantidadeItens { get; }
public PedidoCriadoEvent(Guid pedidoId, string cliente, decimal valor, int quantidadeItens, string version)
: base(pedidoId, version)
{
Cliente = cliente;
Valor = valor;
QuantidadeItens = quantidadeItens;
}
}
// Handler para o Domain Event
public class PedidoCriadoEventHandler : INotificationHandler<PedidoCriadoEvent>
{
private readonly ILogger<PedidoCriadoEventHandler> _logger;
public PedidoCriadoEventHandler(ILogger<PedidoCriadoEventHandler> logger)
{
_logger = logger;
}
public Task Handle(PedidoCriadoEvent notification, CancellationToken cancellationToken)
{
_logger.LogInformation($"Pedido criado: {notification.SourceId} - Cliente: {notification.Cliente} - Valor: {notification.Valor}");
// Implementar lógica adicional (enviar email, atualizar estoque, etc.)
return Task.CompletedTask;
}
}
Exemplos Práticos
Exemplo 1: Sistema de Cadastro de Pessoas
public class CadastrarPessoaService : BaseDomain
{
public CadastrarPessoaService(IMediator mediator, IMapper mapper)
: base(mediator, mapper)
{
}
public async Task<PessoaDto> CadastrarAsync(CadastrarPessoaCommand command)
{
// Validar dados básicos
if (string.IsNullOrEmpty(command.Nome))
{
AddNotification("Nome", "Nome é obrigatório");
}
// Criar e validar CPF/CNPJ
var cpf = new Cpf(command.Cpf);
var cnpj = new Cnpj(command.Cnpj);
var tipoPessoa = new TipoPessoa(command.TipoPessoa, command.DataNascimento);
var documento = new DocumentoPessoa(cpf, cnpj, tipoPessoa);
if (!documento.IsValid())
{
AddNotifications(documento.Notifications);
}
// Validar email
var email = new EmailPessoa(command.Email);
if (!email.IsValid())
{
AddNotifications(email.Notifications);
}
// Se há erros, retornar
if (!IsValid())
{
return null;
}
// Criar entidade
var pessoa = new PessoaEntity
{
Nome = command.Nome,
Documento = documento,
Email = email
};
// Disparar Domain Event
var evento = new PessoaCadastradaEvent(pessoa.Id, pessoa.Nome, "v1.0");
await _mediator.Publish(evento);
// Retornar DTO
return _mapper.Map<PessoaDto>(pessoa);
}
}
Exemplo 2: Validação com Specifications
public class ClienteElegivelParaCreditoSpec : BaseSpecification<ClienteEntity>
{
private readonly decimal _valorSolicitado;
public ClienteElegivelParaCreditoSpec(decimal valorSolicitado)
{
_valorSolicitado = valorSolicitado;
}
public override async Task IsSatisfactory(ClienteEntity entity)
{
// Validar idade
var idade = DateTime.Now.Year - entity.DataNascimento.Year;
if (idade < 18)
{
AddNotification("Idade", "Cliente deve ser maior de idade");
}
// Validar renda
if (entity.RendaMensal < _valorSolicitado * 0.3m)
{
AddNotification("Renda", "Renda insuficiente para o valor solicitado");
}
// Validar histórico (simulação)
if (entity.TemRestricoes)
{
AddNotification("Restricoes", "Cliente possui restrições no CPF");
}
await Task.CompletedTask;
}
}
// Uso no serviço
public class CreditoService : BaseDomain
{
public async Task<bool> AprovarCreditoAsync(ClienteEntity cliente, decimal valor)
{
var spec = new ClienteElegivelParaCreditoSpec(valor);
await spec.IsSatisfactory(cliente);
if (spec.ValidationResult().Any())
{
AddNotifications(spec.ValidationResult());
return false;
}
// Aprovar crédito
return true;
}
}
Exemplo 3: Domain Events com Observabilidade
public class ProcessoPagamentoEvent : DomainEvent<Guid>
{
public decimal Valor { get; }
public string StatusPagamento { get; }
public string FormaPagamento { get; }
public ProcessoPagamentoEvent(Guid pedidoId, decimal valor, string status, string forma, string correlationId)
: base(pedidoId, correlationId)
{
Valor = valor;
StatusPagamento = status;
FormaPagamento = forma;
}
}
// Múltiplos handlers para o mesmo evento
public class LogPagamentoHandler : INotificationHandler<ProcessoPagamentoEvent>
{
public Task Handle(ProcessoPagamentoEvent notification, CancellationToken cancellationToken)
{
// Log estruturado
Log.Information("Pagamento processado {PedidoId} {Valor} {Status} {CorrelationId}",
notification.SourceId,
notification.Valor,
notification.StatusPagamento,
notification.Version);
return Task.CompletedTask;
}
}
public class EmailPagamentoHandler : INotificationHandler<ProcessoPagamentoEvent>
{
public Task Handle(ProcessoPagamentoEvent notification, CancellationToken cancellationToken)
{
// Enviar email de confirmação
if (notification.StatusPagamento == "Aprovado")
{
// Enviar email de sucesso
}
return Task.CompletedTask;
}
}
API Reference
Classes Base
BaseDomain
BaseDomain(IMediator mediator, IMapper mapper)
: Constructor com dependênciasvirtual IList<NotificationR> ValidationResult()
: Retorna lista de notificações
AggregateRoot
- Herda de
DomainEntity
- Utilizada como ponto de entrada da raiz de agregação
DomainEvent<TSourceId>
DomainEvent(TSourceId sourceId, string version)
: Constructor com ID e versãoTSourceId SourceId
: ID da entidade que gerou o eventoDateTimeOffset When
: Data/hora da criação do eventostring Version
: Versão/correlação para rastreamento
Interfaces
IBaseDomain
IList<NotificationR> ValidationResult()
: Contrato para validação
ISpecification<TEntity>
Task IsSatisfactory(TEntity entity)
: Método de validação da specificationIList<NotificationR> ValidationResult()
: Resultado da validação
ICommandR
bool SaveChanges
: Controla se deve salvar no repositóriobool RemoveNotificationsBeginning
: Remove notificações no início do processamento
ICommandResultR
- Interface de marcação para resultados de comandos
Value Objects
CPF
Cpf(string numero)
: Constructor com validaçãostring Codigo
: CPF sem formataçãostring Mascara()
: Retorna CPF formatado (999.999.999-99)const int maxCPF = 11
: Tamanho máximo
CNPJ
Cnpj(string numero)
: Constructor com validaçãostring Codigo
: CNPJ sem formataçãostring Mascara()
: Retorna CNPJ formatado (99.999.999/9999-99)const int maxCNPJ = 14
: Tamanho máximo
EmailPessoa
EmailPessoa(string endereco)
: Constructor com validaçãostring Endereco
: Email validadoconst int maxEndereco = 256
: Tamanho máximo
Endereco
- Constructor com todos os campos obrigatórios
- Propriedades: TipoLogradouro, Logradouro, CodigoMunicipio, NomeMunicipio, UF, Bairro, Cep, Numero, Complemento, SiglaPais
- Constantes estáticas para tamanhos mínimos e máximos
Enums Principais
EnumAtivoInativo
A
: AtivoI
: InativoN
: Todos (para consultas)
EnumFisicaJuridica
F
: Pessoa FísicaJ
: Pessoa Jurídica
EnumSimNao
S
: SimN
: Não
Troubleshooting
Problemas Comuns
MediatR não encontrado
Problema: InvalidOperationException
ao tentar injetar IMediator
Solução: Registrar MediatR no container DI
builder.Services.AddMediatR(cfg => cfg.RegisterServicesFromAssembly(Assembly.GetExecutingAssembly()));
AutoMapper não configurado
Problema: AutoMapperMappingException
em BaseDomain
Solução: Configurar profiles do AutoMapper
builder.Services.AddAutoMapper(typeof(Program));
Value Objects inválidos
Problema: CPF/CNPJ retornando null mesmo com dados corretos
Causa: Validação muito restritiva ou dados com formatação
Solução: Verificar se os dados estão sem formatação
// ✅ Correto
var cpf = new Cpf("12345678901");
// ❌ Incorreto - com formatação
var cpf = new Cpf("123.456.789-01");
Domain Events não funcionando
Problema: Handlers não são executados
Solução: Verificar registro do MediatR e implementação dos handlers
// Registrar assembly com os handlers
builder.Services.AddMediatR(cfg => cfg.RegisterServicesFromAssemblies(
Assembly.GetExecutingAssembly(),
Assembly.GetAssembly(typeof(SeuDomainEventHandler))
));
Logs e Debugging
Para debugging das validações:
public class MinhaClasseDominio : BaseDomain
{
public void ValidarComLog()
{
// Seu código de validação
var erros = ValidationResult();
foreach (var erro in erros)
{
Console.WriteLine($"Campo: {erro.Property}, Mensagem: {erro.Message}");
}
}
}
Changelog
Ver arquivo CHANGELOG.md para histórico detalhado de alterações.
📞 Suporte
Para dúvidas, issues ou contribuições:
- 🐛 Issues: GitHub Issues
- 📧 Email: suporte@zocate.li
- 📖 Documentação: Wiki do Projeto
Nuuvify CommonPack - Construindo soluções robustas para .NET 🚀
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
.NET Core | netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
.NET Standard | netstandard2.1 is compatible. |
MonoAndroid | monoandroid was computed. |
MonoMac | monomac was computed. |
MonoTouch | monotouch was computed. |
Tizen | tizen60 was computed. |
Xamarin.iOS | xamarinios was computed. |
Xamarin.Mac | xamarinmac was computed. |
Xamarin.TVOS | xamarintvos was computed. |
Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.1
- AutoMapper (>= 12.0.1)
- MediatR (>= 12.2.0)
- Nuuvify.CommonPack.Extensions (>= 2.1.0-test.25100503)
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last Updated |
---|---|---|
3.0.0-test.25052502 | 0 | 5/25/2025 |
3.0.0-test.25050204 | 0 | 5/2/2025 |
3.0.0-test.25042801 | 0 | 4/28/2025 |
3.0.0-test.25042712 | 0 | 4/28/2025 |
3.0.0-test.25042711 | 0 | 4/28/2025 |
3.0.0-test.25042709 | 0 | 4/28/2025 |
3.0.0-test.25042708 | 0 | 4/28/2025 |
3.0.0-test.25042707 | 0 | 4/28/2025 |
3.0.0-test.25042705 | 0 | 4/28/2025 |
3.0.0-test.25042703 | 0 | 4/28/2025 |
3.0.0-test.25042701 | 0 | 4/27/2025 |
3.0.0-test.25041702 | 1 | 4/17/2025 |
2.1.0-test.25100702 | 1 | 10/8/2025 |
2.1.0-test.25100602 | 9 | 10/6/2025 |
2.1.0-test.25100507 | 1 | 10/6/2025 |
2.1.0-test.25100503 | 1 | 10/5/2025 |
2.1.0-test.25093008 | 1 | 9/30/2025 |
2.0.0-preview.25041508 | 0 | 4/16/2025 |
2.0.0-preview.25041506 | 18 | 4/16/2025 |