SlideShare une entreprise Scribd logo
1  sur  42
Globalcode – Open4education
Bolovo 2.0: Indo do EJB 2.0 ao
Domain Driven Design
Alexandre Rodrigues
Globalcode – Open4education
O que é
BOLOVO?
Globalcode – Open4education
BOLOVO é um termo criado em 2007
por Paulo Silveira (Caelum) e Phillip
Calçado (Buoyant/DigitalOcean) para
uma apresentação na JustJava
Globalcode – Open4education
Layer Objects
(JSP/JSF)
Business Objects
(EJB)
Values Objects
(EntityBeans)
Globalcode – Open4education
Detalhe:
Values Objects
NÃO SÃO
Values Objects!
Globalcode – Open4education
Value Objects são objetos imutáveis, conforme
o padrão definido por Martin Fowler. Isso entra
em conflito com o padrão definido no Core
J2EE. Como a definição criada por Martin
Fowler é anterior ao padrão J2EE, esse
prevaleceu sendo o padrão J2EE renomeados
para Transfer Objects - TO.
https://martinfowler.com/bliki/ValueObject.html
http://www.oracle.com/technetwork/java/transferobject-139757.html
Globalcode – Open4education
Layer Objects
(JSP/JSF)
Business Objects
(EJB)
Values Objects
Transfer Objects
(EntityBeans)
Globalcode – Open4education
E como isso ocorre
na prática?
Globalcode – Open4education
Dado um usuário
O nome não pode estar em branco
O e-mail deve ser válido
O e-mail não pode estar cadastrado para outro
usuário
Deve permitir exclusão lógica
@Entity
public class UsuarioTO {
@Id
private long id;
@Column
private String nome;
@Column
private String email;
@Column
private boolean excluido;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getNome() {
return nome;
}
public void setNome(String nome) {
this.nome = nome;
}
public String getEmail() {
return email;
}
...
}
@Statelles
public class UsuarioBO {
@Inject
private UsuarioDAO usuarioDAO;
public void adicionar(UsuarioTO usuarioTO) {
validarDadosDoUsuario(usuarioTO);
}
public UsuarioTO alterar(UsuarioTO usuarioTO) {
validarDadosDoUsuario(usuarioTO);
if (usuarioFoiExcluido(usuarioTO.getId()))
throw new BusinessException("Não é possível alterar o usuário id {0} - Usuário Excluido", usuarioTO.getEmail());
return usuarioDAO.atualizar(usuarioTO);
}
public void excluir(long id) {
UsuarioTO usuario = usuarioDAO.buscarPelo(id);
usuario.setExcluido(true);
}
private void validarDadosDoUsuario(UsuarioTO usuarioTO) {
if (isBlank(usuarioTO.getNome()))
throw new BusinessException("O nome do usuário não pode estar em branco");
if (emailInvalido(usuarioTO.getEmail()))
throw new BusinessException("O e-mail informado {0} é inválido", usuarioTO.getEmail());
if (emailJaCadastradoParaOutroUsuario(usuarioTO))
throw new BusinessException("Email {0} já cadastrado para outro usuário", usuarioTO.getEmail());
}
...
}
Globalcode – Open4education
Mas qual é o
problema com essa
abordagem?
Globalcode – Open4education
Domínio
Anêmico
Globalcode – Open4education
Separando dados das
regras de negócio
voltamos ao modelo
procedural!!!!!
Globalcode – Open4education
Então Surgiu o Domain
Driven Design e novos
padrões de arquitetura
corporativa!!!
Globalcode – Open4education
Agora utilizamos
Services e POJOs!
Chegamos ao fim do
domínio anêmico!
Globalcode – Open4education
SQN!!!!
@Entity
public class Usuario {
@Id
private long id;
@Column
private String nome;
@Column
private String email;
@Column
private boolean excluido;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getNome() {
return nome;
}
public void setNome(String nome) {
this.nome = nome;
}
public String getEmail() {
return email;
}
...
}
@Service
public class UsuarioService {
@Autowired
private UsuarioDAO usuarioDAO;
public void adicionar(Usuario usuario) {
validarDadosDoUsuario(usuario);
}
public Usuario alterar(Usuario usuario) {
validarDadosDoUsuario(usuario);
if (usuarioFoiExcluido(usuario.getId()))
throw new BusinessException("Não é possível alterar o usuário id {0} - Usuário Excluido", usuario.getEmail());
return usuarioDAO.atualizar(usuario);
}
public void excluir(long id) {
Usuario usuario = usuarioDAO.buscarPelo(id);
usuario.setExcluido(true);
}
private void validarDadosDoUsuario(Usuario usuario) {
if (isBlank(usuario.getNome()))
throw new BusinessException("O nome do usuário não pode estar em branco");
if (emailInvalido(usuario.getEmail()))
throw new BusinessException("O e-mail informado {0} é inválido", usuario.getEmail());
if (emailJaCadastradoParaOutroUsuario(usuario))
throw new BusinessException("Email {0} já cadastrado para outro usuário", usuario.getEmail());
}
...
}
Globalcode – Open4education
Controllers Serviços
Entidades – POJOS
(EntityBeans)
Globalcode – Open4education
BOLOVO 2.0
Globalcode – Open4education
Mas por que fazemos isso?
Porque é natural
Porque facilita a injeção de dependências
Porque a maioria dos frameworks
web utilizam JavaBeans!!!
Globalcode – Open4education
E como mudar isso?
Orientação a objetos de fato – SOLID
Utilização de padrões para criação de aplicações
corporativas:
Domain-Driven Design
P of EAA – (Fowler Patterns)
Globalcode – Open4education
Abuse dos
Objects Values
@Embeddable
public class EMail {
@Column
private String endereco;
private EMail(){};
public EMail(String endereco) {
EmailValidator validator = EmailValidator.getInstance();
if (!validator.isValid(endereco))
throw new IllegalArgumentException(String.format("O endereço de e-mail '%s' informado é inválido", endereco));
this.endereco = endereco;
}
@Override
public String toString() {
return endereco;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
EMail eMail = (EMail) o;
return endereco.equals(eMail.endereco);
}
@Override
public int hashCode() {
return endereco.hashCode();
}
}
Globalcode – Open4education
Respeite sempre as
invariantes!!!
@Entity
public class Usuario {
@Id @GeneratedValue(strategy=AUTO)
private long id;
...
private Usuario() {};
public Usuario(String nome, EMail eMail) {
definirDadosDoUsuario(nome, eMail);
EntityManager entityManager = Component.get(EntityManager.class);
entityManager.persist(this);
}
public void alterar(String nome, EMail eMail) {
if (excluido)
throw new IllegalStateException("Não é possivel alterar os dados de um usuário excluido");
definirDadosDoUsuario(nome, eMail);
}
private void definirDadosDoUsuario(String nome, EMail eMail) {
if (StringUtils.isBlank(nome))
throw new IllegalArgumentException("O nome não pode estar em branco");
if (eMail == null)
throw new IllegalArgumentException("O eMail não pode ser nulo");
if (emailEmUsoPorOutroUsuario(eMail))
throw new IllegalArgumentException(String.format("O e-mail '%s' já está em uso por outro usuário", eMail));
this.nome = nome;
this.eMail = eMail;
}
...
}
Globalcode – Open4education
Cuidado!
Uma Entidade do DDD não
necessariamente é uma
entidade JPA
@Entity
public class Usuario {
...
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Usuario usuario = (Usuario) o;
return id == usuario.id;
}
@Override
public int hashCode() {
return (int) (id ^ (id >>> 32));
}
...
}
Globalcode – Open4education
Dica: Evite expor o ID
da entidade para o
usuário
Globalcode – Open4education
Separe os serviços
corretamente
@Service
public class ServicoDePersistenciaDoUsuario {
@Autowired
private EntityManager entityManager;
@Transactional
public Usuario criarUsuario(String nome, EMail eMail) {
return new Usuario(nome, eMail);
}
@Transactional
public void alterarDadosDoUsuario(long id, String nome, EMail eMail) {
Usuario usuario = entityManager.find(Usuario.class, id);
usuario.alterar(nome, eMail);
}
@Transactional
public boolean excluirUsuario(long id) {
Usuario usuario = entityManager.find(Usuario.class, id);
if (usuario != null) {
usuario.excluir();
return true;
}
return false;
}
}
@Repository
public class UsuarioRepositorio {
@Autowired
private EntityManager entityManager;
public Optional<Long> buscarIdDoUsuarioPorEmail(EMail eMail) {
...
}
public Optional<Usuario> buscarPeloId(long id) {
...
}
public List<Usuario> listar() {
...
}
}
Globalcode – Open4education
Evite Getters e
Setters nas classes
do domínio.
@Embeddable
public class EMail {
@Column
private String endereco;
private EMail(){};
public EMail(String endereco) {
EmailValidator validator = EmailValidator.getInstance();
if (!validator.isValid(endereco))
throw new IllegalArgumentException(String.format("O endereço de e-mail '%s' informado é inválido", endereco));
this.endereco = endereco;
}
@Override
public String toString() {
return endereco;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
EMail eMail = (EMail) o;
return endereco.equals(eMail.endereco);
}
@Override
public int hashCode() {
return endereco.hashCode();
}
}
@Entity
public class Usuario {
...
public long id() {
return id;
}
public String nome() {
return nome;
}
public EMail eMail() {
return eMail;
}
public boolean excluido() {
return excluido;
}
public void excluir() {
excluido = true;
}
...
}
Globalcode – Open4education
Se não uso Getters e
Setters como mapear
meu domínio?
public class DadosDoUsuario {
private long id;
@NotNull @NotBlank
private String nome;
@Email
private String email;
public DadosDoUsuario() {}
public static DadosDoUsuario criarComBaseNo(Usuario usuario) {
DadosDoUsuario dadosDoUsuario = new DadosDoUsuario();
dadosDoUsuario.id = usuario.id();
dadosDoUsuario.nome = usuario.nome();
dadosDoUsuario.email = usuario.eMail().toString();
return dadosDoUsuario;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getNome() {
return nome;
}
public void setNome(String nome) {
this.nome = nome;
}
...
}
Globalcode – Open4education
Faça a transformação
na camada de acesso
@RestController
@RequestMapping("/usuario")
public class UsuarioController {
@Autowired
private UsuarioRepositorio repositorio;
@Autowired
private ServicoDePersistenciaDoUsuario servico;
@GetMapping(path="/{id}", produces="application/json; charset=UTF-8")
public ResponseEntity<DadosDoUsuario> buscarUsuario(@PathVariable("id") long id) {
Optional<Usuario> usuario = repositorio.buscarPeloId(id);
if (usuario.isPresent()) {
DadosDoUsuario dados = DadosDoUsuario.criarComBaseNo(usuario.get());
return ResponseEntity.ok(dados);
return ResponseEntity.notFound().build();
}
@GetMapping(path="/listar", produces="application/json; charset=UTF-8")
public List<DadosDoUsuario> listar() {
Stream<Usuario> clientes = repositorio.listar().stream();
return clientes.map(DadosDoUsuario::criarComBaseNo).collect(toList());
}
@PutMapping(produces="application/json; charset=UTF-8")
public ResponseEntity<DadosDeRetorno> inserir(@Validated @RequestBody DadosDoUsuario dados, Errors errors) {
if (errors.hasErrors()) {
DadosDeRetorno retorno = DadosDeRetorno.criarCom(dados.getId(), errors.getFieldErrors());
return ResponseEntity.badRequest().body(retorno);
}
Usuario usuario = servico.criarUsuario(dados.getNome(), new EMail(dados.getEmail()));
DadosDeRetorno retorno = DadosDeRetorno.criarCom(usuario.id());
URI uri = URI.create("/cliente/" + dados.getId());
return ResponseEntity.created(uri).body(retorno);
}
...
Globalcode – Open4education
Algumas Vantagens
Separação de reponsabilidades bem definidas
Facilita o desenvolvimento e evolução do sistema
Fim do Open Session In View Anti-Pattern
Fim do LazyInitializationException
Fim de campos @Transient nas entidades
alexandre-rodrigues-35788ba5
https://bitbucket.org/alexandre_rodrigues_poa/tdc-bolovo/
alexandre.rodrigues.poa@gmail.com

Contenu connexe

Similaire à TDC Florianópolis 2018 - Bolovo 2.0: Indo do EJB 2.0 ao Domain Driven Design

Enriquecendo um Modelo de Domínio Anêmico
Enriquecendo um Modelo de Domínio AnêmicoEnriquecendo um Modelo de Domínio Anêmico
Enriquecendo um Modelo de Domínio AnêmicoThoughtWorks Brasil
 
Como Apresentar Codigo em Slides - Javou #7 - 2016
Como Apresentar Codigo em Slides - Javou #7 - 2016Como Apresentar Codigo em Slides - Javou #7 - 2016
Como Apresentar Codigo em Slides - Javou #7 - 2016Rafael Ponte
 
Introducao ao Spring Web MVC
Introducao ao Spring Web MVCIntroducao ao Spring Web MVC
Introducao ao Spring Web MVCEder Magalhães
 
Como conectar programas em linguagem java a bases de dados
Como conectar programas em linguagem java  a bases de dadosComo conectar programas em linguagem java  a bases de dados
Como conectar programas em linguagem java a bases de dadosHenrique Fernandes
 
Plataforma de compiladores .NET, C# 6 e Visual Studio 2015
Plataforma de compiladores .NET, C# 6 e Visual Studio 2015Plataforma de compiladores .NET, C# 6 e Visual Studio 2015
Plataforma de compiladores .NET, C# 6 e Visual Studio 2015Rogério Moraes de Carvalho
 
Play Framework - FLISOL
Play Framework - FLISOLPlay Framework - FLISOL
Play Framework - FLISOLgrupoweblovers
 
Java EE 6 JPA 2.0, EJB 3.1 e CDI 1.0
Java EE 6 JPA 2.0, EJB 3.1 e CDI 1.0Java EE 6 JPA 2.0, EJB 3.1 e CDI 1.0
Java EE 6 JPA 2.0, EJB 3.1 e CDI 1.0Elvis Rocha
 
AspectJ — Programação orientada a aspectos em Java
AspectJ — Programação orientada a aspectos em JavaAspectJ — Programação orientada a aspectos em Java
AspectJ — Programação orientada a aspectos em Javaelliando dias
 
Javascript para CSharpers 4 - POO
Javascript para CSharpers 4 - POOJavascript para CSharpers 4 - POO
Javascript para CSharpers 4 - POOWesley Lemos
 
Porque você deveria usar CDI nos seus projetos Java! - JavaOne LA 2012 - Sérg...
Porque você deveria usar CDI nos seus projetos Java! - JavaOne LA 2012 - Sérg...Porque você deveria usar CDI nos seus projetos Java! - JavaOne LA 2012 - Sérg...
Porque você deveria usar CDI nos seus projetos Java! - JavaOne LA 2012 - Sérg...Caelum
 
Minicurso kotlin no desenvolvimento mobile - UTFPR
Minicurso kotlin no desenvolvimento mobile - UTFPRMinicurso kotlin no desenvolvimento mobile - UTFPR
Minicurso kotlin no desenvolvimento mobile - UTFPRLucas Antonio Ramos Sartori
 
VRaptor - Alta produtividade no Desenvolvimento Web em Java
VRaptor - Alta produtividade no Desenvolvimento Web em JavaVRaptor - Alta produtividade no Desenvolvimento Web em Java
VRaptor - Alta produtividade no Desenvolvimento Web em JavaDaniel Faria Gomes
 

Similaire à TDC Florianópolis 2018 - Bolovo 2.0: Indo do EJB 2.0 ao Domain Driven Design (20)

VRaptor4
VRaptor4VRaptor4
VRaptor4
 
Enriquecendo um Modelo de Domínio Anêmico
Enriquecendo um Modelo de Domínio AnêmicoEnriquecendo um Modelo de Domínio Anêmico
Enriquecendo um Modelo de Domínio Anêmico
 
Como Apresentar Codigo em Slides - Javou #7 - 2016
Como Apresentar Codigo em Slides - Javou #7 - 2016Como Apresentar Codigo em Slides - Javou #7 - 2016
Como Apresentar Codigo em Slides - Javou #7 - 2016
 
Introducao ao Spring Web MVC
Introducao ao Spring Web MVCIntroducao ao Spring Web MVC
Introducao ao Spring Web MVC
 
Javascript
JavascriptJavascript
Javascript
 
Spring MVC - QConSP
Spring MVC - QConSPSpring MVC - QConSP
Spring MVC - QConSP
 
Como conectar programas em linguagem java a bases de dados
Como conectar programas em linguagem java  a bases de dadosComo conectar programas em linguagem java  a bases de dados
Como conectar programas em linguagem java a bases de dados
 
Orientação a Objetos (3)
Orientação a Objetos (3)Orientação a Objetos (3)
Orientação a Objetos (3)
 
Spring Data Jpa
Spring Data JpaSpring Data Jpa
Spring Data Jpa
 
Plataforma de compiladores .NET, C# 6 e Visual Studio 2015
Plataforma de compiladores .NET, C# 6 e Visual Studio 2015Plataforma de compiladores .NET, C# 6 e Visual Studio 2015
Plataforma de compiladores .NET, C# 6 e Visual Studio 2015
 
Play Framework - FLISOL
Play Framework - FLISOLPlay Framework - FLISOL
Play Framework - FLISOL
 
Solid
SolidSolid
Solid
 
Java EE 6 JPA 2.0, EJB 3.1 e CDI 1.0
Java EE 6 JPA 2.0, EJB 3.1 e CDI 1.0Java EE 6 JPA 2.0, EJB 3.1 e CDI 1.0
Java EE 6 JPA 2.0, EJB 3.1 e CDI 1.0
 
AspectJ — Programação orientada a aspectos em Java
AspectJ — Programação orientada a aspectos em JavaAspectJ — Programação orientada a aspectos em Java
AspectJ — Programação orientada a aspectos em Java
 
Javascript para CSharpers 4 - POO
Javascript para CSharpers 4 - POOJavascript para CSharpers 4 - POO
Javascript para CSharpers 4 - POO
 
Porque você deveria usar CDI nos seus projetos Java! - JavaOne LA 2012 - Sérg...
Porque você deveria usar CDI nos seus projetos Java! - JavaOne LA 2012 - Sérg...Porque você deveria usar CDI nos seus projetos Java! - JavaOne LA 2012 - Sérg...
Porque você deveria usar CDI nos seus projetos Java! - JavaOne LA 2012 - Sérg...
 
Clean code
Clean codeClean code
Clean code
 
Minicurso kotlin UTFPR
Minicurso kotlin UTFPR Minicurso kotlin UTFPR
Minicurso kotlin UTFPR
 
Minicurso kotlin no desenvolvimento mobile - UTFPR
Minicurso kotlin no desenvolvimento mobile - UTFPRMinicurso kotlin no desenvolvimento mobile - UTFPR
Minicurso kotlin no desenvolvimento mobile - UTFPR
 
VRaptor - Alta produtividade no Desenvolvimento Web em Java
VRaptor - Alta produtividade no Desenvolvimento Web em JavaVRaptor - Alta produtividade no Desenvolvimento Web em Java
VRaptor - Alta produtividade no Desenvolvimento Web em Java
 

TDC Florianópolis 2018 - Bolovo 2.0: Indo do EJB 2.0 ao Domain Driven Design

  • 1. Globalcode – Open4education Bolovo 2.0: Indo do EJB 2.0 ao Domain Driven Design Alexandre Rodrigues
  • 3. Globalcode – Open4education BOLOVO é um termo criado em 2007 por Paulo Silveira (Caelum) e Phillip Calçado (Buoyant/DigitalOcean) para uma apresentação na JustJava
  • 4. Globalcode – Open4education Layer Objects (JSP/JSF) Business Objects (EJB) Values Objects (EntityBeans)
  • 5. Globalcode – Open4education Detalhe: Values Objects NÃO SÃO Values Objects!
  • 6. Globalcode – Open4education Value Objects são objetos imutáveis, conforme o padrão definido por Martin Fowler. Isso entra em conflito com o padrão definido no Core J2EE. Como a definição criada por Martin Fowler é anterior ao padrão J2EE, esse prevaleceu sendo o padrão J2EE renomeados para Transfer Objects - TO. https://martinfowler.com/bliki/ValueObject.html http://www.oracle.com/technetwork/java/transferobject-139757.html
  • 7. Globalcode – Open4education Layer Objects (JSP/JSF) Business Objects (EJB) Values Objects Transfer Objects (EntityBeans)
  • 8. Globalcode – Open4education E como isso ocorre na prática?
  • 9. Globalcode – Open4education Dado um usuário O nome não pode estar em branco O e-mail deve ser válido O e-mail não pode estar cadastrado para outro usuário Deve permitir exclusão lógica
  • 10. @Entity public class UsuarioTO { @Id private long id; @Column private String nome; @Column private String email; @Column private boolean excluido; public long getId() { return id; } public void setId(long id) { this.id = id; } public String getNome() { return nome; } public void setNome(String nome) { this.nome = nome; } public String getEmail() { return email; } ... }
  • 11. @Statelles public class UsuarioBO { @Inject private UsuarioDAO usuarioDAO; public void adicionar(UsuarioTO usuarioTO) { validarDadosDoUsuario(usuarioTO); } public UsuarioTO alterar(UsuarioTO usuarioTO) { validarDadosDoUsuario(usuarioTO); if (usuarioFoiExcluido(usuarioTO.getId())) throw new BusinessException("Não é possível alterar o usuário id {0} - Usuário Excluido", usuarioTO.getEmail()); return usuarioDAO.atualizar(usuarioTO); } public void excluir(long id) { UsuarioTO usuario = usuarioDAO.buscarPelo(id); usuario.setExcluido(true); } private void validarDadosDoUsuario(UsuarioTO usuarioTO) { if (isBlank(usuarioTO.getNome())) throw new BusinessException("O nome do usuário não pode estar em branco"); if (emailInvalido(usuarioTO.getEmail())) throw new BusinessException("O e-mail informado {0} é inválido", usuarioTO.getEmail()); if (emailJaCadastradoParaOutroUsuario(usuarioTO)) throw new BusinessException("Email {0} já cadastrado para outro usuário", usuarioTO.getEmail()); } ... }
  • 12. Globalcode – Open4education Mas qual é o problema com essa abordagem?
  • 14. Globalcode – Open4education Separando dados das regras de negócio voltamos ao modelo procedural!!!!!
  • 15. Globalcode – Open4education Então Surgiu o Domain Driven Design e novos padrões de arquitetura corporativa!!!
  • 16. Globalcode – Open4education Agora utilizamos Services e POJOs! Chegamos ao fim do domínio anêmico!
  • 18. @Entity public class Usuario { @Id private long id; @Column private String nome; @Column private String email; @Column private boolean excluido; public long getId() { return id; } public void setId(long id) { this.id = id; } public String getNome() { return nome; } public void setNome(String nome) { this.nome = nome; } public String getEmail() { return email; } ... }
  • 19. @Service public class UsuarioService { @Autowired private UsuarioDAO usuarioDAO; public void adicionar(Usuario usuario) { validarDadosDoUsuario(usuario); } public Usuario alterar(Usuario usuario) { validarDadosDoUsuario(usuario); if (usuarioFoiExcluido(usuario.getId())) throw new BusinessException("Não é possível alterar o usuário id {0} - Usuário Excluido", usuario.getEmail()); return usuarioDAO.atualizar(usuario); } public void excluir(long id) { Usuario usuario = usuarioDAO.buscarPelo(id); usuario.setExcluido(true); } private void validarDadosDoUsuario(Usuario usuario) { if (isBlank(usuario.getNome())) throw new BusinessException("O nome do usuário não pode estar em branco"); if (emailInvalido(usuario.getEmail())) throw new BusinessException("O e-mail informado {0} é inválido", usuario.getEmail()); if (emailJaCadastradoParaOutroUsuario(usuario)) throw new BusinessException("Email {0} já cadastrado para outro usuário", usuario.getEmail()); } ... }
  • 20. Globalcode – Open4education Controllers Serviços Entidades – POJOS (EntityBeans)
  • 22. Globalcode – Open4education Mas por que fazemos isso? Porque é natural Porque facilita a injeção de dependências Porque a maioria dos frameworks web utilizam JavaBeans!!!
  • 23. Globalcode – Open4education E como mudar isso? Orientação a objetos de fato – SOLID Utilização de padrões para criação de aplicações corporativas: Domain-Driven Design P of EAA – (Fowler Patterns)
  • 25. @Embeddable public class EMail { @Column private String endereco; private EMail(){}; public EMail(String endereco) { EmailValidator validator = EmailValidator.getInstance(); if (!validator.isValid(endereco)) throw new IllegalArgumentException(String.format("O endereço de e-mail '%s' informado é inválido", endereco)); this.endereco = endereco; } @Override public String toString() { return endereco; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; EMail eMail = (EMail) o; return endereco.equals(eMail.endereco); } @Override public int hashCode() { return endereco.hashCode(); } }
  • 26. Globalcode – Open4education Respeite sempre as invariantes!!!
  • 27. @Entity public class Usuario { @Id @GeneratedValue(strategy=AUTO) private long id; ... private Usuario() {}; public Usuario(String nome, EMail eMail) { definirDadosDoUsuario(nome, eMail); EntityManager entityManager = Component.get(EntityManager.class); entityManager.persist(this); } public void alterar(String nome, EMail eMail) { if (excluido) throw new IllegalStateException("Não é possivel alterar os dados de um usuário excluido"); definirDadosDoUsuario(nome, eMail); } private void definirDadosDoUsuario(String nome, EMail eMail) { if (StringUtils.isBlank(nome)) throw new IllegalArgumentException("O nome não pode estar em branco"); if (eMail == null) throw new IllegalArgumentException("O eMail não pode ser nulo"); if (emailEmUsoPorOutroUsuario(eMail)) throw new IllegalArgumentException(String.format("O e-mail '%s' já está em uso por outro usuário", eMail)); this.nome = nome; this.eMail = eMail; } ... }
  • 28. Globalcode – Open4education Cuidado! Uma Entidade do DDD não necessariamente é uma entidade JPA
  • 29. @Entity public class Usuario { ... @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Usuario usuario = (Usuario) o; return id == usuario.id; } @Override public int hashCode() { return (int) (id ^ (id >>> 32)); } ... }
  • 30. Globalcode – Open4education Dica: Evite expor o ID da entidade para o usuário
  • 31. Globalcode – Open4education Separe os serviços corretamente
  • 32. @Service public class ServicoDePersistenciaDoUsuario { @Autowired private EntityManager entityManager; @Transactional public Usuario criarUsuario(String nome, EMail eMail) { return new Usuario(nome, eMail); } @Transactional public void alterarDadosDoUsuario(long id, String nome, EMail eMail) { Usuario usuario = entityManager.find(Usuario.class, id); usuario.alterar(nome, eMail); } @Transactional public boolean excluirUsuario(long id) { Usuario usuario = entityManager.find(Usuario.class, id); if (usuario != null) { usuario.excluir(); return true; } return false; } }
  • 33. @Repository public class UsuarioRepositorio { @Autowired private EntityManager entityManager; public Optional<Long> buscarIdDoUsuarioPorEmail(EMail eMail) { ... } public Optional<Usuario> buscarPeloId(long id) { ... } public List<Usuario> listar() { ... } }
  • 34. Globalcode – Open4education Evite Getters e Setters nas classes do domínio.
  • 35. @Embeddable public class EMail { @Column private String endereco; private EMail(){}; public EMail(String endereco) { EmailValidator validator = EmailValidator.getInstance(); if (!validator.isValid(endereco)) throw new IllegalArgumentException(String.format("O endereço de e-mail '%s' informado é inválido", endereco)); this.endereco = endereco; } @Override public String toString() { return endereco; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; EMail eMail = (EMail) o; return endereco.equals(eMail.endereco); } @Override public int hashCode() { return endereco.hashCode(); } }
  • 36. @Entity public class Usuario { ... public long id() { return id; } public String nome() { return nome; } public EMail eMail() { return eMail; } public boolean excluido() { return excluido; } public void excluir() { excluido = true; } ... }
  • 37. Globalcode – Open4education Se não uso Getters e Setters como mapear meu domínio?
  • 38. public class DadosDoUsuario { private long id; @NotNull @NotBlank private String nome; @Email private String email; public DadosDoUsuario() {} public static DadosDoUsuario criarComBaseNo(Usuario usuario) { DadosDoUsuario dadosDoUsuario = new DadosDoUsuario(); dadosDoUsuario.id = usuario.id(); dadosDoUsuario.nome = usuario.nome(); dadosDoUsuario.email = usuario.eMail().toString(); return dadosDoUsuario; } public long getId() { return id; } public void setId(long id) { this.id = id; } public String getNome() { return nome; } public void setNome(String nome) { this.nome = nome; } ... }
  • 39. Globalcode – Open4education Faça a transformação na camada de acesso
  • 40. @RestController @RequestMapping("/usuario") public class UsuarioController { @Autowired private UsuarioRepositorio repositorio; @Autowired private ServicoDePersistenciaDoUsuario servico; @GetMapping(path="/{id}", produces="application/json; charset=UTF-8") public ResponseEntity<DadosDoUsuario> buscarUsuario(@PathVariable("id") long id) { Optional<Usuario> usuario = repositorio.buscarPeloId(id); if (usuario.isPresent()) { DadosDoUsuario dados = DadosDoUsuario.criarComBaseNo(usuario.get()); return ResponseEntity.ok(dados); return ResponseEntity.notFound().build(); } @GetMapping(path="/listar", produces="application/json; charset=UTF-8") public List<DadosDoUsuario> listar() { Stream<Usuario> clientes = repositorio.listar().stream(); return clientes.map(DadosDoUsuario::criarComBaseNo).collect(toList()); } @PutMapping(produces="application/json; charset=UTF-8") public ResponseEntity<DadosDeRetorno> inserir(@Validated @RequestBody DadosDoUsuario dados, Errors errors) { if (errors.hasErrors()) { DadosDeRetorno retorno = DadosDeRetorno.criarCom(dados.getId(), errors.getFieldErrors()); return ResponseEntity.badRequest().body(retorno); } Usuario usuario = servico.criarUsuario(dados.getNome(), new EMail(dados.getEmail())); DadosDeRetorno retorno = DadosDeRetorno.criarCom(usuario.id()); URI uri = URI.create("/cliente/" + dados.getId()); return ResponseEntity.created(uri).body(retorno); } ...
  • 41. Globalcode – Open4education Algumas Vantagens Separação de reponsabilidades bem definidas Facilita o desenvolvimento e evolução do sistema Fim do Open Session In View Anti-Pattern Fim do LazyInitializationException Fim de campos @Transient nas entidades