Árvore de páginas

Versões comparadas

Chave

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

Índice

Introdução

Para trabalhar com nossa camada de domínio precisamos definir nossas entidades, suas validações, criar nossos repositórios e serviços de domínio.

...

As entidades são um dos conceitos fundamentais do DDD (Domain Driven Design) onde Eric Evans descreve-o como "um objeto que não é fundamentalmente definido por seus atributos, mas sim por um fio de continuidade e identidade".

No TNF, entidades 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.

Note que o exemplo acima está marcado como internal para que ela não seja exposta para fora da camada de domínio.

...

Em um de seus artigos descrevendo o uso do Notification pattern, Martin Flowler faz uma citação sobre o uso de exception dentro de uma aplicação descrita pelos autores Dave Thomas Andy Hunt em seu livro "The Pragmatic Programmer: From Journeyman to Master" (versão traduzida): "Acreditamos que exceções raramente devem ser usadas como parte de um fluxo normal em um programa: exceções devem ser reservadas para eventos inesperados".

No TNF pensamos da mesma forma: não devemos usar exceção para simples validações de nossas entidades de domínio.

Podemos resumir o notification pattern como uma estrutura (representado como representada por um DTO no TNF) que coleta erros e os expõe de forma tratadas as aos diversos níveis da aplicação.

...

Dentro do pacote Tnf podemos utilizar através da herança a classe Specification<>  Specification<Entity> que recebe como parâmetro a entidade a ser validada.

Pensa Pense na especificação como sendo uma regra para sua entidade. Sendo assim a A regra acima valida para que o campo "Name" de nossa classe Person não seja inválido.

...

O pattern de Builder é usado para construir nossa entidade de negocio aqui no TNF, não apenas isso, mas também agregar todas as especificações e retornar uma estrutura informado se a entidade é valida ou não para ser utilizada.

Pense no Builder como um agregador de regras para a entidade de negocionegócio.

Podemos ver sua definição para a entidade criada Person disponível a seguir:

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> Build()
        {
            var shouldHaveName = new PersonNameShouldHaveValueSpecification();
            if (!shouldHaveName.IsSatisfiedBy(Instance))
            {
                var notificationMessage =
                    LocalizationHelper.GetString(AppConsts.LocalizationSourceName,
                    Person.Error.PersonNameMustHaveValue);

                Response.AddNotification(Person.Error.PersonNameMustHaveValue, notificationMessage);
            }

            return base.Build();
        }
    }
}

Note que o 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.

...

Também podemos ver no exemplo acima que se a regra da especificação testada não é valida (IsSatisfiedBy) utilizamos a classe LocalizationHelper para obter a mensagem de erro através da localização do TNF, adicionando ela ao objeto de Response que contém todas as notificações do builder.

...

Nossa camada de domínio tem a responsabilidade de implementar a interface (contrato) de nosso repositório deixando para que outra camada de infraestrutura realize sua implementação.

Esta camada não conheço conheçe a fonte da informação obtida/persistida (Relacional, NoSQL, Arquivo, etc).

O TNF possui uma interface chamada IRepository que fará A interface IRepository fará a injeção de dependência pela convenção de quem a implementar.

...

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

Note que ao 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

Quando falamos em serviço de domínio podemos falar em processos de negócio. Um serviço de domínio tem a característica de representar esses processos de forma clara e definida utilizando toda a infraestrutura necessária (Repositórios).

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

...

O serviço por default recebe um repositório podendo conter um ou mais de acordo com a necessidade.Abaixo temos a definição de um serviço:

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> Register(PersonDto dto);
    }
}

...

Bloco de código
languagec#
firstline1
titleRegistrationService.cs
linenumberstrue
using Tnf.Architecture.Domain.Interfaces.Services;
using Tnf.Domain.Services;
using Tnf.Dto;

namespace Tnf.Architecture.Domain.Registration
{
    public class RegistrationService : DomainService<IPersonRepository>, IRegistrationService
    {
        public RegistrationService(IPersonRepository repository) : base(repository) 
		{ 
		}
        public DtoResponseBase<PersonDto> Register(PersonDto dto)
        {
            var response = new DtoResponseBase<PersonDto>();
            var builder = new PersonBuilder()
                   .WithName(dto.Name);
 
            var build = builder.Build();
            if (!build.Success)
            {
                response.AddNotifications(build.Notifications);
                return response;
            }
            var id = Repository.CreatePerson(dto);
            dto.Id = id;
            return response;
        }
    }
}

Perceba que no No inicio do método Register definimos o objeto de retorno e logo após criamos o PersonBuilder para construir nossa entidade validando ela logo após sua criação pelo método 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 Caso e caso existam notificações retornadas do Build agregamos elas ao nosso retorno.

...

Neste exemplo temos apenas um repositório, mas lembre-se que temos presente a injeção de dependência do TNF framework e podemos usar quantos forem necessários em nosso serviço de domínio.