...
Entidades são derivadas de classe Entity no TNF onde esta já possui uma chave (do tipo int ou informada através de um parâmetro genérico):
Quando a entidade é criada também é definido um enumerador, com códigos únicos que descrevem os erros de negócio da entidade.
Bloco de código | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
using System; using Tnf.Domain.Entities; namespace Tnf.Architecture.Domain.Registration { internal class Person : Entity { public string Name { get; internal set; } public DateTime CreationTime { get; internal set; } public Person() => CreationTime = DateTime.Now; public enum Error { Unexpected = 0, PersonIdMustHaveValue = 1, PersonNameMustHaveValue = 2 } } } |
Quando a entidade é criada também é definido um enumerador, com códigos únicos que descrevem os erros de negócio da entidade.
Note que o exemplo acima está marcado como internal para que ela não seja exposta para fora da camada de domínio.
...
Bloco de código | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
using System;
using System.Linq.Expressions;
using Tnf.Specifications;
namespace Tnf.Architecture.Domain.Registration.Specifications
{
internal class PersonNameShouldHaveValueSpecification : Specification<Person>
{
public override string LocalizationSource => AppConsts.LocalizationSourceName;
public override Enum LocalizationKey => Professional.Error.ProfessionalNameMustHaveValue;
public override Expression<Func<Person, bool>> ToExpression()
{
return (p) => !string.IsNullOrWhiteSpace(p.Name);
}
}
} |
Dentro do pacote Tnf Tnf.App.Specifications podemos utilizar através da herança a classe Specification<Entity> que recebe como parâmetro a entidade a ser validada.
...
Bloco de código | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
using Tnf.Architecture.CrossCutting; using Tnf.Architecture.Domain.Registration.Specifications; using Tnf.Builder; using Tnf.Localization; namespace Tnf.Architecture.Domain.Registration { internal class PersonBuilder : Builder<Person> { public PersonBuilder() : base() { } public PersonBuilder(Person instance) : base(instance) { } public PersonBuilder WithId(int id) { Instance.Id = id; return this; } public PersonBuilder WithName(string name) { Instance.Name = name; return this; } public override BuilderResponse<Person>Person Build() { var shouldHaveName = new PersonNameShouldHaveValueSpecificationbase.Validate(); ifreturn (!shouldHaveNamebase.IsSatisfiedByBuild(Instance)); { var notificationMessage =} protected override LocalizationHelper.GetString(AppConsts.LocalizationSourceName,void Specifications() Person.Error.PersonNameMustHaveValue); { Response.AddNotification(Person.Error.PersonNameMustHaveValue, notificationMessage); } return base.Build(AddSpecification(new PersonNameShouldHaveValueSpecification()); } } } |
O builder acima cria a entidade de forma fluente, adicionando valores a entidade e através do método sobrescrito Build executando todas as regras (especificações) de nossa entidade.
A estrutura retornada pelo método de Build é do Tipo BuilderResponse<Person>.
Esse objeto BuilderResponse vem do pacote Tnf.Dto e possibilita que utilizemos assim o Notification pattern para retornar as validações da entidade como notificações de negocio evitando o uso de exceções.
Também podemos ver no exemplo acima que se a regra da especificação testada não é valida (IsSatisfiedBy) utilizamos Podemos ver no exemplo acima que chamamos o método Validate da classe pai para executar todas as Specifications adicionadas no método sobrescrito Specifications() e se a regra das especificações adicionadas não for valida, ele adicionará uma Notification no objeto único do request com a mensagem especificada pelos campos LocalizationSource e LocalizationKey utilizando a classe LocalizationHelper para obter a mensagem de erro através da localização, adicionando ela ao objeto de Response que contém todas as notificações do builder.
A classe PersonBuilder também está marcada como internal para que tanto a entidade, regras e builder estejam fechadas dentro de nossa camada de domínio.
...
Para trabalhar com serviços de domínio, temos a interface IDomainService e uma classe chamada DomainService<IRepository>AppDomainService<IRepository> que recebe por parâmetro genérico um repositório.
...
Bloco de código | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
using Tnf.Domain.Services; using Tnf.Dto; namespace Tnf.Architecture.Domain.Interfaces.Services { public interface IRegistrationService : IDomainService { DtoResponseBase<PersonDto>PersonDto Register(PersonDto dto); } } |
Podemos perceber a herança da interface IDomainService e o retorno do objeto do método Register DtoResponseBasePersonDto.
Esse objeto de resposta irá retornar as validações da entidade caso ela não esteja apta a ser cadastradao objeto enviado com o id que foi salvo no banco.
O código a seguir exemplifica a implementação de nosso serviço de domínio, usando o repositório e o builder criado para a entidade Person.
Bloco de código | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
using Tnf.Architecture.Domain.Interfaces.Services; using Tnf.App.Domain.Services; using Tnf.Dto; namespace Tnf.Architecture.Domain.Registration { public class RegistrationService : DomainService<IPersonRepository>AppDomainService<IPersonRepository>, IRegistrationService { public RegistrationService(IPersonRepository repository) : base(repository) { } public DtoResponseBase<PersonDto>PersonDto Register(PersonDto dto) { var response = new DtoResponseBase<PersonDto>(); var builder = new PersonBuilder() .WithName(dto.Name); var buildperson = builder.Build(); if (!buildNotification.SuccessHasNotification()) { var response.AddNotifications(build.Notificationsid = Repository.CreatePerson(person); return response; } var id = Repository.CreatePerson(dto)dto.Id = id; dto.Id = id;} return responsedto; } } } |
No inicio do método Register definimos o objeto de retorno e logo após criamos o PersonBuilder para construir nossa entidade através da chamada do método Build.
O método Build nos retorna a estrutura de DTO informando se contém ou não notificações presentes na entidade através da propriedade Success e caso existam notificações retornadas do Build agregamos elas ao nosso retornoda Entidade e se foi levantado notificações durante o build podemos verificar se existe notificações com o campo exposto pela classe Tnf.App.Domain.Services.AppDomainService Notification.
Quando a entidade é apta a ser utilizada podemos consumir nosso repositório através da propriedade Repository que corresponde a IPersonRepository recebida pela injeção de interface no construtor da classe herdada DomainService<IPersonRepository>AppDomainService<IPersonRepository>.
Neste exemplo temos apenas um repositório, mas lembre-se que temos presente a injeção de dependência do framework e podemos usar quantos forem necessários em nosso serviço de domínio.