Árvore de páginas

Versões comparadas

Chave

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

...

Com o projeto criado vamos adicionar os seguintes pacotes via nuget: Tnf.App.Builder, Tnf.App.Dto, Tnf Builder disponível em nosso package source: https://www.myget.org/F/tnf/api/v3/index.json

...

Bloco de código
languagec#
firstline1
titlePerson.cs
linenumberstrue
using System;
using Tnf.Domain.Entities;

namespace Tnf.Architecture.Domain.Registration
{
    internalpublic 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
        }
    }
}

Note que o exemplo acima está marcado como internal public para que ela não seja exposta para fora da camada de domínio, por exemplo, na camada de Mapper e de Repository.

Validação de Entidades 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 { get; protected set; } => AppConsts.LocalizationSourceName;
        public override Enum LocalizationKey =>{ get; protected set; } = Person.Error.PersonNameMustHaveValue;
 
        public override Expression<Func<Person, bool>> ToExpression()
        {
            return (p) => !string.IsNullOrWhiteSpace(p.Name);
        }
    }
}

...

Bloco de código
languagec#
firstline1
titlePersonBuilder.cs
linenumberstrue
using Tnf.Architecture.CrossCutting;
using Tnf.Architecture.Domain.Registration.Specifications;
using Tnf.App.Builder;
using Tnf.Localization;

namespace Tnf.Architecture.Domain.Registration
{
    internalpublic class PersonBuilder : Builder<Person>
    {
        public PersonBuilder(INotificationHandler notification) : base(notification) { }
        public PersonBuilder(INotificationHandler notification, Person instance) : base(notification, instance) { }

        public PersonBuilder WithId(int id)
        {
            Instance.Id = id;
            return this;
        }

        public PersonBuilder WithName(string name)
        {
            Instance.Name = name;
            return this;
        }

        public override Person Build()
        {
            base.Validate();
            return base.Build();
        }
 
        protected override void Specifications()
        {
            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.

Podemos ver no exemplo acima que chamamos o método Validate da classe pai Internamente é chamado o método Validate da classe Builder 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.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.

Definindo um Repositório

...

Bloco de código
languagec#
firstline1
titleIPersonRepository.cs
linenumberstrue
public interface IPersonRepository : IRepository
{
	int CreatePerson(PersonDtoPerson personDtoperson);
}

Ao definir nosso repositório não passamos a entidade de domínio em sua declaração e sim um DTO que a representa. O uso do DTO é para que a entidade de domínio não seja exposta e assim dessa forma sua integridade seja mantida.

Criando um Serviço de Domínio

...

Para trabalhar com serviços de domínio, temos a interface IDomainService e uma classe chamada AppDomainService<IRepository> que recebe por parâmetro genérico um repositório.

Tanto a interface como a classe base quando implementadas já utilizam as convenções ao realizar a injeção da dependência automaticamente.

...

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
    {
        PersonDtoint Register(PersonDtoPersonBuilder dtobuilder);
    }
}

Podemos perceber a herança da interface IDomainService e o retorno do objeto do método Register PersonDto.Esse objeto de resposta irá retornar o objeto enviado com o id que foi salvo no bancosendo o tipo da chave primária da entidade no método Register, ele retornará só a chave que recebe do repositório pois a camada de Application  já possui o Dto de retorno mas não possui o valor da cahve primária.

O método Register recebe um PersonBuilder que a camada de Application irá mandar preenchido e quem chamará o método Build é a própria camada de Domain para que só a essa camada mexa com a entidade.

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 : AppDomainService<IPersonRepository>, IRegistrationService
    {
		private readonly IPersonRepository _repository;
        public RegistrationService(IPersonRepository repository) : base(repository) 
		{
			_repository = repository;
		}
        public PersonDtoint Register(PersonDtoPersonBuilder dtobuilder)
        {
            var builder = new PersonBuilder()
                   .WithName(dto.Name);
 
            var person = builder.Build();
            if (!Notification.HasNotification())
            {				return 0;
                var id = Repositoryreturn _repository.CreatePerson(person);
                dto.Id = id;
            }
            return dto;
        }
    }
}

No inicio do método Register definimos o objeto de retorno e logo após criamos o PersonBuilder para chamamos o método Build do Builder para construir nossa entidade através da chamada do método Build.

O método Build nos retorna a estrutura da 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.

...