Árvore de páginas

Versões comparadas

Chave

  • Esta linha foi adicionada.
  • Esta linha foi removida.
  • A formatação mudou.

...

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
languagec#
firstline1
titlePerson.cs
linenumberstrue
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
languagec#
firstline1
titlePersonNameShouldHaveValueSpecification.cs
linenumberstrue
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
languagec#
firstline1
titlePersonBuilder.cs
linenumberstrue
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
languagec#
firstline1
titleIRegistrationService.cs
linenumberstrue
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
languagec#
firstline1
titleRegistrationService.cs
linenumberstrue
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.