Usando o DDD (Domain-Driven Design) com a Ajuda do Flow
Exemplos de Prompts e Seus Resultados (Com Análise)
Exemplo de Prompt Ineficiente
Prompt:
Me dê um exemplo de código Java para um sistema de feedback usando DDD.
O resultado pode ser algo como:
// FeedbackService.java
import org.springframework.stereotype.Service;
// ... outras importações
@Service
public class FeedbackService {
public void criarFeedback(String colaboradorEmail, String avaliadorEmail, String mensagem) {
// Lógica de criação de feedback simples
System.out.println("Feedback criado para " + colaboradorEmail + " de " + avaliadorEmail + ": " + mensagem);
}
public void aprovarFeedback(Long feedbackId) {
// Lógica de aprovação de feedback
System.out.println("Feedback " + feedbackId + " aprovado.");
}
}
// FeedbackRepository.java (Interface simples)
public interface FeedbackRepository {
// Métodos CRUD básicos
void save(Object feedback);
Object findById(Long id);
}
// Feedback.java (Classe simples)
public class Feedback {
private Long id;
private String colaboradorEmail;
private String avaliadorEmail;
private String mensagem;
// Getters e Setters
}
Deficiências do Prompt
- Falta de Especificidade: O prompt é genérico ("sistema de feedback usando DDD"). Não especifica qual parte do sistema, quais entidades, quais ações de domínio são relevantes.
- Omite Conceitos Chave: Não menciona os conceitos específicos do DDD que deveriam ser aplicados (Entity, Value Object, Repository, Domain Service).
- Sem Contexto de Negócio: Não há descrição de regras de negócio, fluxos ou interações importantes do domínio.
Deficiências do Código Gerado
- DDD Superficial: O código não demonstra a aplicação real dos conceitos de DDD:
Feedback.javaparece ser uma Entity, mas não há clareza na distinção de sua identidade ou métodos de comportamento.FeedbackRepositoryé uma interface genérica (Object feedback) e não específica para um agregado ou entidade de domínio.FeedbackServiceé um Application Service (ou até mesmo um Infrastructure Service disfarçado), não um Domain Service puro, pois contém lógica de "criação simples" e "aprovação" que deveriam ser comportamentos mais ricos nas entidades ou orquestradas por um Domain Service que interage com múltiplos agregados. A lógica de negócio está anêmica e não encapsulada nas entidades.
- Não Utiliza Value Objects: Não há indicação de Value Objects para encapsular tipos de dados como e-mails, o que é uma prática comum em DDD para garantir imutabilidade e validade.
- Acoplamento: A
FeedbackServicediretamente manipula strings e IDs, sem interagir com Entities ricas em comportamento.
Exemplo de Prompt Eficiente
Prompt:
Considerando um sistema de feedback onde um colaborador solicita feedback de avaliadores externos, e um PDM aprova essa solicitação. Modele em código Java os seguintes conceitos do DDD:
1. **Entity**: Um `FormularioDeFeedback` que possui um ID único, perguntas personalizadas e o status (pendente aprovação, aguardando respostas, finalizado).
2. **Value Object**: Uma `Pergunta` com texto e tipo, e um `AvaliadorExterno` com nome e email, sendo ambos imutáveis.
3. **Repository**: Um `FormularioDeFeedbackRepository` para persistir e buscar formulários por ID, e listar formulários pendentes para aprovação do PDM.
4. **Domain Service**: Um `ServicoDeAprovacaoDeFormulario` que orquestra a lógica de negócio de aprovar ou rejeitar um `FormularioDeFeedback` pelo PDM, alterando seu status e notificando os avaliadores se aprovado, ou o colaborador se rejeitado.
Forneça apenas os esqueletos das classes e interfaces, sem lógica de persistência de banco de dados detalhada nos repositórios, focando na estrutura do domínio.
Resultado desse prompt:
// src/main/java/com/example/domain/model/FormularioDeFeedback.java
package com.example.domain.model;
import com.example.domain.service.ServicoDeAprovacaoDeFormulario;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
public class FormularioDeFeedback {
private UUID id;
private String colaboradorId; // ID do Colaborador que solicitou
private List<Pergunta> perguntas;
private List<AvaliadorExterno> avaliadores;
private StatusFormulario status;
private LocalDateTime dataCriacao;
private LocalDateTime dataAprovacao; // Para controle de prazo de resposta
public enum StatusFormulario {
EM_CRIACAO, PENDENTE_APROVACAO, AGUARDANDO_RESPOSTAS, FINALIZADO, REJEITADO
}
// Construtor
public FormularioDeFeedback(String colaboradorId, List<Pergunta> perguntas, List<AvaliadorExterno> avaliadores) {
this.id = UUID.randomUUID();
this.colaboradorId = colaboradorId;
this.perguntas = perguntas != null ? List.copyOf(perguntas) : Collections.emptyList();
this.avaliadores = avaliadores != null ? List.copyOf(avaliadores) : Collections.emptyList();
this.status = StatusFormulario.EM_CRIACAO;
this.dataCriacao = LocalDateTime.now();
}
// Métodos de comportamento da Entity
public void enviarParaAprovacao() {
if (this.status == StatusFormulario.EM_CRIACAO || this.status == StatusFormulario.REJEITADO) {
this.status = StatusFormulario.PENDENTE_APROVACAO;
// Notificação ao PDM será orquestrada pelo serviço de aplicação ou domínio
} else {
throw new IllegalStateException("Formulário não pode ser enviado para aprovação no status atual: " + this.status);
}
}
public void aprovar() {
if (this.status == StatusFormulario.PENDENTE_APROVACAO) {
this.status = StatusFormulario.AGUARDANDO_RESPOSTAS;
this.dataAprovacao = LocalDateTime.now();
} else {
throw new IllegalStateException("Formulário não pode ser aprovado no status atual: " + this.status);
}
}
public void rejeitar(String motivo) {
if (this.status == StatusFormulario.PENDENTE_APROVACAO) {
this.status = StatusFormulario.REJEITADO;
// O motivo pode ser armazenado em algum lugar se necessário
} else {
throw new IllegalStateException("Formulário não pode ser rejeitado no status atual: " + this.status);
}
}
public void finalizar() {
if (this.status == StatusFormulario.AGUARDANDO_RESPOSTAS) {
this.status = StatusFormulario.FINALIZADO;
} else {
throw new IllegalStateException("Formulário não pode ser finalizado no status atual: " + this.status);
}
}
// Getters
public UUID getId() { return id; }
public String getColaboradorId() { return colaboradorId; }
public List<Pergunta> getPerguntas() { return perguntas; }
public List<AvaliadorExterno> getAvaliadores() { return avaliadores; }
public StatusFormulario getStatus() { return status; }
public LocalDateTime getDataCriacao() { return dataCriacao; }
public LocalDateTime getDataAprovacao() { return dataAprovacao; }
}
// src/main/java/com/example/domain/model/Pergunta.java
package com.example.domain.model;
import java.util.Objects;
public class Pergunta {
private final String texto;
private final TipoPergunta tipo; // Ex: ABERTA, MULTIPLA_ESCOLHA
public enum TipoPergunta {
ABERTA, MULTIPLA_ESCOLHA, SIM_NAO
}
public Pergunta(String texto, TipoPergunta tipo) {
if (texto == null || texto.trim().isEmpty()) {
throw new IllegalArgumentException("Texto da pergunta não pode ser vazio.");
}
this.texto = texto;
this.tipo = tipo;
}
public String getTexto() { return texto; }
public TipoPergunta getTipo() { return tipo; }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Pergunta pergunta = (Pergunta) o;
return Objects.equals(texto, pergunta.texto) && tipo == pergunta.tipo;
}
@Override
public int hashCode() {
return Objects.hash(texto, tipo);
}
}
// src/main/java/com/example/domain/model/AvaliadorExterno.java
package com.example.domain.model;
import java.util.Objects;
import java.util.regex.Pattern;
public class AvaliadorExterno {
private final String nome;
private final String email; // Pode ser um Value Object Email
private static final Pattern EMAIL_PATTERN = Pattern.compile("^[A-Za-z0-9+_.-]+@(.+)$");
public AvaliadorExterno(String nome, String email) {
if (nome == null || nome.trim().isEmpty()) {
throw new IllegalArgumentException("Nome do avaliador não pode ser vazio.");
}
if (email == null || !EMAIL_PATTERN.matcher(email).matches()) {
throw new IllegalArgumentException("Email do avaliador inválido.");
}
// Exemplo de regra de negócio: e-mails internos não são permitidos
if (email.contains("@ciandt.com.br")) {
throw new IllegalArgumentException("Avaliadores externos não podem ter e-mail CI&T.");
}
this.nome = nome;
this.email = email;
}
public String getNome() { return nome; }
public String getEmail() { return email; }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
AvaliadorExterno that = (AvaliadorExterno) o;
return Objects.equals(nome, that.nome) && Objects.equals(email, that.email);
}
@Override
public int hashCode() {
return Objects.hash(nome, email);
}
}
// src/main/java/com/example/domain/repository/FormularioDeFeedbackRepository.java
package com.example.domain.repository;
import com.example.domain.model.FormularioDeFeedback;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
public interface FormularioDeFeedbackRepository {
void save(FormularioDeFeedback formulario);
Optional<FormularioDeFeedback> findById(UUID id);
List<FormularioDeFeedback> findPendentesParaAprovacaoDoPdm(String pdmId);
// Outros métodos de busca conforme necessidade do domínio
}
// src/main/java/com/example/domain/service/ServicoDeAprovacaoDeFormulario.java
package com.example.domain.service;
import com.example.domain.model.AvaliadorExterno;
import com.example.domain.model.FormularioDeFeedback;
import com.example.domain.repository.FormularioDeFeedbackRepository;
import java.util.List;
import java.util.UUID;
public class ServicoDeAprovacaoDeFormulario {
private final FormularioDeFeedbackRepository formularioRepository;
// O ideal seria injetar um serviço de notificação aqui
// private final NotificacaoService notificacaoService;
public ServicoDeAprovacaoDeFormulario(FormularioDeFeedbackRepository formularioRepository /*, NotificacaoService notificacaoService*/) {
this.formularioRepository = formularioRepository;
// this.notificacaoService = notificacaoService;
}
public void aprovarFormulario(UUID formularioId, String pdmId) {
FormularioDeFeedback formulario = formularioRepository.findById(formularioId)
.orElseThrow(() -> new IllegalArgumentException("Formulário não encontrado."));
// Regra de negócio: apenas o PDM responsável pode aprovar
// if (!formulario.getPdmId().equals(pdmId)) {
// throw new SecurityException("PDM não autorizado a aprovar este formulário.");
// }
formulario.aprovar();
formularioRepository.save(formulario); // Persiste a mudança de status
// Notificar avaliadores
// for (AvaliadorExterno avaliador : formulario.getAvaliadores()) {
// notificacaoService.enviarEmailAvaliador(avaliador.getEmail(), formulario.getId());
// }
System.out.println("Formulário " + formularioId + " aprovado. Avaliadores notificados.");
}
public void rejeitarFormulario(UUID formularioId, String pdmId, String motivo) {
FormularioDeFeedback formulario = formularioRepository.findById(formularioId)
.orElseThrow(() -> new IllegalArgumentException("Formulário não encontrado."));
// Regra de negócio: apenas o PDM responsável pode rejeitar
// if (!formulario.getPdmId().equals(pdmId)) {
// throw new SecurityException("PDM não autorizado a rejeitar este formulário.");
// }
formulario.rejeitar(motivo);
formularioRepository.save(formulario); // Persiste a mudança de status
// Notificar colaborador
// notificacaoService.enviarEmailColaborador(formulario.getColaboradorId(), "rejeitado", motivo);
System.out.println("Formulário " + formularioId + " rejeitado. Colaborador notificado.");
}
}
Eficiência do Prompt
- Clareza e Contexto de Domínio: O prompt inicia com uma descrição clara do cenário de negócio ("sistema de feedback onde um colaborador solicita feedback... e um PDM aprova"). Isso estabelece o contexto ubíquo necessário para o DDD.
- Especificidade dos Conceitos DDD: Ele solicita explicitamente a modelagem de Entity, Value Object, Repository e Domain Service, garantindo que o foco da resposta seja na aplicação desses padrões.
- Definição Detalhada de Cada Conceito: Para cada conceito, são fornecidas características e exemplos específicos (
FormularioDeFeedbackcom ID único e status para Entity,PerguntaeAvaliadorExternoimutáveis para Value Object, métodos específicos paraFormularioDeFeedbackRepository, e a orquestração de lógica noServicoDeAprovacaoDeFormulario). Isso direciona a IA a criar classes com o comportamento e a estrutura corretos. - Restrições e Exclusões: A instrução para fornecer "apenas os esqueletos das classes e interfaces, sem lógica de persistência de banco de dados detalhada nos repositórios" é crucial. Isso evita que a IA adicione detalhes de infraestrutura desnecessários e mantém o foco no domínio.
- Comportamento Enfatizado: Ao descrever o
ServicoDeAprovacaoDeFormulario, o prompt detalha as ações que ele deve orquestrar ("aprovar ou rejeitar, alterando status e notificando"), incentivando a criação de métodos com lógica de domínio.
Qualidade do Código Gerado
- Entity Rica: A classe
FormularioDeFeedbacké uma Entity bem definida com umUUIDcomo identificador, e métodos de comportamento (enviarParaAprovacao,aprovar,rejeitar,finalizar) que modificam seu próprio estado (status), demonstrando encapsulamento da lógica de negócio. - Value Objects Imutáveis:
PerguntaeAvaliadorExternosão modelados como Value Objects com todos os camposfinal, construtores que realizam validações e implementamequals()ehashCode(), garantindo sua imutabilidade e baseados em valor. A validação de e-mail e a regra de negócio para e-mails CI&T dentro deAvaliadorExternodemonstram um Value Object rico. - Repository Abstrato:
FormularioDeFeedbackRepositoryé uma interface que define operações de persistência e recuperação específicas para aFormularioDeFeedback, sem expor detalhes de implementação de banco de dados, alinhado com o padrão Repository. - Domain Service Orquestrador:
ServicoDeAprovacaoDeFormularioatua como um Domain Service legítimo. Ele injeta oFormularioDeFeedbackRepositorypara obter a Entity, chama métodos de comportamento na própria Entity (formulario.aprovar(),formulario.rejeitar()) e orquestra a notificação externa, sem conter a lógica de negócio intrínseca ao formulário ou avaliador. Ele gerencia o fluxo de aprovação que envolve várias entidades. - Estrutura de Pacotes: O código sugere uma boa organização de pacotes (
domain.model,domain.repository,domain.service), o que é fundamental em projetos DDD.
Conclusão
Este exemplo demonstra que para obter um código alinhado com os princípios do Domain-Driven Design de uma IA, a precisão e a riqueza de detalhes no prompt são indispensáveis. Ao especificar claramente o contexto do domínio, os conceitos do DDD a serem aplicados (Entity, Value Object, Repository, Domain Service), e até mesmo as regras de negócio e comportamentos esperados dentro de cada conceito, a IA é capaz de gerar um modelo de domínio que realmente reflete a filosofia do DDD.
A deficiência principal em prompts genéricos reside na incapacidade da IA de inferir a intencionalidade do domínio e a separação de responsabilidades que são características do DDD. Um prompt bem elaborado atua como um "designer" para a IA, guiando-a para construir um modelo que encapsula a lógica de negócio nas entidades certas, usa objetos de valor para clareza e imutabilidade, abstrai a persistência e orquestra operações complexas através de serviços de domínio, resultando em um design de software muito mais robusto e compreensível.