Índice |
---|
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 | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
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 | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
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.
...
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 | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
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 | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
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.