O documento discute a evolução da arquitetura de aplicações Java EE do padrão EJB 2.0 para o Domain Driven Design. Apresenta como os objetos de valor, objetos de negócio e objetos de transferência foram utilizados no passado e como o Domain Driven Design traz domínios mais ricos através de entidades e serviços.
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
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
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());
}
...
}
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());
}
...
}
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)
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;
}
...
}
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));
}
...
}
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