┌─────────────────────────────────────────────────────────────────┐
│ API Layer (REST) │
│ PaymentController.java │
└────────────────────────┬───────────────────────────────────────┘
│
┌────────────────────────┴───────────────────────────────────────┐
│ Application Layer (CQRS) │
├─────────────────────────────┬───────────────────────────────────┤
│ PaymentCommandService │ PaymentQueryService │
│ (Write Side) │ (Read Side) │
└────────────────────────┬────┴──────────────────┬────────────────┘
│ │
┌────────────────────────┴───────────────────────┴────────────────┐
│ Domain Layer (DDD) │
│ │
│ ┌──────────────┐ ┌─────────────────────────────────┐ │
│ │ Payment │────▶│ Domain Events │ │
│ │ (Aggregate) │ │ - PaymentCreatedEvent │ │
│ └──────────────┘ │ - PaymentValidatedEvent │ │
│ │ - PaymentAuthorizedEvent │ │
│ │ - PaymentCapturedEvent │ │
│ │ - PaymentCompletedEvent │ │
│ │ - PaymentFailedEvent │ │
│ │ - PaymentRefundedEvent │ │
│ └─────────────────────────────────┘ │
└────────────────────────┬───────────────────────────────────────┘
│
┌────────────────────────┴───────────────────────────────────────┐
│ Infrastructure Layer │
│ │
│ ┌──────────────────┐ ┌──────────────────────┐ │
│ │ EventStore │──────────────│ EventStoreRepository │ │
│ │ (Serialization) │ │ (JPA) │ │
│ └──────────────────┘ └──────────────────────┘ │
│ │ │
│ ┌─────────┴─────────┐ │
│ │ EventStoreEntity │ │
│ │ (Database) │ │
│ └───────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
Em vez de armazenar apenas o estado atual, armazenamos todos os eventos que levaram a esse estado.
CRUD Tradicional:
Payment Table:
┌────┬──────────┬────────┬───────────┐
│ ID │ Customer │ Amount │ Status │
├────┼──────────┼────────┼───────────┤
│ 1 │ CUST-123 │ 100.00 │ COMPLETED │ ← Apenas estado final
└────┴──────────┴────────┴───────────┘
Event Sourcing:
Event Store:
┌────┬─────────────────────┬─────────┬────────────────────────────┐
│ ID │ Event Type │ Version │ Payload │
├────┼─────────────────────┼─────────┼────────────────────────────┤
│ 1 │ PaymentCreated │ 0 │ {customer: "CUST-123", ... │
│ 2 │ PaymentValidated │ 1 │ {...} │
│ 3 │ PaymentAuthorized │ 2 │ {authCode: "AUTH-123"} │
│ 4 │ PaymentCaptured │ 3 │ {txnId: "TXN-456"} │
│ 5 │ PaymentCompleted │ 4 │ {...} │
└────┴─────────────────────┴─────────┴────────────────────────────┘
↓
Replay eventos para reconstruir estado
↓
Payment(status=COMPLETED, version=4)
-
Auditoria Completa
- Sabemos exatamente o que aconteceu e quando
- Compliance e regulamentação
-
Time-Travel Debugging
- Podemos ver o estado em qualquer ponto no tempo
- Útil para investigar bugs
-
Replay de Eventos
- Podemos reprocessar eventos
- Criar novas projeções
-
Event-Driven Architecture
- Base para microsserviços
- Comunicação assíncrona
Separar operações de escrita (Commands) e leitura (Queries).
Write Side (Commands):
@Service
public class PaymentCommandService {
public PaymentDTO createPayment(CreatePaymentCommand cmd) {
Payment payment = Payment.create(...);
eventStore.save(payment); // Gera eventos
return toDTO(payment);
}
public void processPayment(String id) {
Payment payment = eventStore.load(id);
payment.validate();
payment.authorize(...);
payment.capture(...);
payment.complete();
eventStore.save(payment); // Persiste eventos
}
}Read Side (Queries):
@Service
public class PaymentQueryService {
public PaymentDTO getPayment(String id) {
Payment payment = eventStore.load(id);
return toDTO(payment);
}
public PaymentHistoryDTO getHistory(String id) {
List<Events> events = eventStore.loadEvents(id);
return buildHistory(events);
}
}-
Escalabilidade
- Write e Read podem escalar independentemente
- Otimização específica para cada lado
-
Performance
- Read models podem ser otimizados (cache, denormalização)
- Writes focam em consistência
-
Flexibilidade
- Múltiplas projeções do mesmo dado
- Diferentes modelos para diferentes casos de uso
Coordenar transações distribuídas através de uma série de transações locais.
public void processPayment(String paymentId) {
Payment payment = eventStore.load(paymentId);
try {
// Step 1: Validate
payment.validate();
eventStore.save(payment);
// Step 2: Authorize
String authCode = externalAuthService.authorize(...);
payment.authorize(authCode);
eventStore.save(payment);
// Step 3: Capture
String txnId = externalPaymentGateway.capture(...);
payment.capture(txnId);
eventStore.save(payment);
// Step 4: Complete
payment.complete();
eventStore.save(payment);
} catch (Exception e) {
// Compensating Transaction
payment.fail(e.getMessage(), "SAGA_FAILURE");
eventStore.save(payment);
throw e;
}
}1. Orchestration (usado no projeto)
- Um orquestrador central coordena o fluxo
- Mais controle, mais acoplamento
2. Choreography
- Cada serviço publica eventos
- Outros serviços reagem
- Menos controle, menos acoplamento
1. Aggregate Root
public class Payment {
private String id; // Aggregate ID
private PaymentStatus status;
private List<DomainEvent> uncommittedEvents;
// Business logic
public void authorize(String code) {
if (status != VALIDATED) {
throw new IllegalStateException("...");
}
// Apply event
}
}2. Value Objects
public enum PaymentStatus {
PENDING, VALIDATED, AUTHORIZED, ...
}3. Domain Events
public abstract class DomainEvent {
private final String eventId;
private final Instant occurredAt;
private final String aggregateId;
// ...
}4. Repository Pattern
public interface EventStoreRepository
extends JpaRepository<EventStoreEntity, Long> {
List<EventStoreEntity> findByAggregateId(String id);
}Pontos Fracos:
- ❌ Sem histórico de mudanças
- ❌ Arquitetura simples demais
- ❌ Sem testes
- ❌ Persistência em arquivo texto
- ❌ Sem API REST
- ❌ Conceitos básicos apenas
Código:
public class Biblioteca {
private ArrayList<Livro> livros;
public void emprestar(int id) {
for (Livro l : livros) {
if (l.getId() == id) {
l.setEmprestado(true);
salvar(); // Sobrescreve arquivo
return;
}
}
}
}Pontos Fortes:
- ✅ Event Sourcing - histórico completo
- ✅ CQRS - escalabilidade
- ✅ Saga Pattern - transações distribuídas
- ✅ DDD - design rico de domínio
- ✅ Spring Boot - framework enterprise
- ✅ Testes automatizados
- ✅ API REST documentada
- ✅ Docker ready
- ✅ Patterns avançados
Código:
@Service
public class PaymentCommandService {
public void processPayment(String id) {
Payment payment = eventStore.load(id);
// Saga Pattern
payment.validate();
payment.authorize(authCode);
payment.capture(txnId);
payment.complete();
// Event Sourcing
eventStore.save(payment);
}
}P: O que é Event Sourcing?
R: É armazenar mudanças de estado como uma sequência de eventos em vez de apenas o estado atual. Permite auditoria completa e replay de eventos.
P: Quando usar Event Sourcing?
R: Quando precisar de:
- Auditoria completa
- Time-travel debugging
- Event-driven architecture
- Compliance regulatório
P: Por que separar Commands e Queries?
R: Para escalar independentemente, otimizar cada lado separadamente, e ter flexibilidade em modelos de dados diferentes.
P: Como garantir consistência em transações distribuídas?
R: Usando Saga Pattern com compensating transactions. Se algo falha, executamos ações de compensação para desfazer mudanças.
P: O que é um Aggregate?
R: É um cluster de objetos de domínio que são tratados como uma unidade para mudanças de dados. Tem um Aggregate Root que garante consistência.
- Event Sourcing básico
- CQRS simples
- Saga local
- Snapshots para performance
- Projections (Read Models)
- Kafka/RabbitMQ integration
- Distributed Saga
- Event-driven microservices
- CQRS com bancos separados
- Event Store clustering
-
Livros:
- "Domain-Driven Design" - Eric Evans
- "Implementing Domain-Driven Design" - Vaughn Vernon
- "Microservices Patterns" - Chris Richardson
-
Artigos:
- Martin Fowler - Event Sourcing
- Martin Fowler - CQRS
- Microservices.io - Saga Pattern
-
Cursos:
- DDD & Event Sourcing (Pluralsight)
- Spring Boot Microservices (Udemy)