❓ Qué problema resuelve

  • Modelos complejos: Cuando el modelo de lectura y escritura tienen diferentes necesidades
  • Performance: Optimización independiente de consultas y comandos
  • Escalabilidad: Escalar lecturas y escrituras por separado
  • Complejidad de consultas: Queries complejas que no encajan en el modelo de dominio

🔧 Cómo funciona

CQRS separa las operaciones de lectura (Queries) de las operaciones de escritura (Commands), permitiendo optimizar cada lado independientemente.

Conceptos Clave:

  • Commands: Operaciones que modifican el estado
  • Queries: Operaciones que leen datos
  • Command Handlers: Procesan comandos
  • Query Handlers: Procesan consultas
  • Read Models: Modelos optimizados para lectura
  • Write Models: Modelos optimizados para escritura

📊 Diagrama de Arquitectura

graph TB
    subgraph "Client Applications"
        UI[User Interface]
        API[API Clients]
    end
    
    subgraph "CQRS Application"
        subgraph "Command Side (Write)"
            CMD[Commands]
            CH[Command Handlers]
            WM[Write Model]
            WDB[(Write Database)]
        end
        
        subgraph "Query Side (Read)"
            QRY[Queries]
            QH[Query Handlers]
            RM[Read Model]
            RDB[(Read Database)]
        end
        
        subgraph "Event Store"
            ES[Event Store]
            EP[Event Projections]
        end
    end
    
    UI --> CMD
    UI --> QRY
    API --> CMD
    API --> QRY
    
    CMD --> CH
    CH --> WM
    WM --> WDB
    WM --> ES
    
    QRY --> QH
    QH --> RM
    RM --> RDB
    
    ES --> EP
    EP --> RM

🏗️ Estructura de Paquetes CQRS

src/main/java/com/company/ecommerce/
├── command/                         # Command Side (Write)
│   ├── handler/
│   │   ├── CreateProductCommandHandler.java
│   │   ├── UpdateProductCommandHandler.java
│   │   ├── DeleteProductCommandHandler.java
│   │   └── CommandHandler.java
│   ├── model/                       # Write Models
│   │   ├── Product.java
│   │   ├── ProductRepository.java
│   │   └── ProductAggregate.java
│   ├── dto/
│   │   ├── CreateProductCommand.java
│   │   ├── UpdateProductCommand.java
│   │   └── DeleteProductCommand.java
│   └── validation/
│       ├── ProductCommandValidator.java
│       └── CommandValidator.java
├── query/                           # Query Side (Read)
│   ├── handler/
│   │   ├── GetProductQueryHandler.java
│   │   ├── GetProductsQueryHandler.java
│   │   ├── SearchProductsQueryHandler.java
│   │   └── QueryHandler.java
│   ├── model/                       # Read Models
│   │   ├── ProductView.java
│   │   ├── ProductListView.java
│   │   ├── ProductSearchView.java
│   │   └── ProductReadRepository.java
│   ├── dto/
│   │   ├── GetProductQuery.java
│   │   ├── GetProductsQuery.java
│   │   └── SearchProductsQuery.java
│   └── projection/                  # Event Projections
│       ├── ProductProjection.java
│       ├── ProductListProjection.java
│       └── ProjectionHandler.java
├── event/                           # Events & Event Store
│   ├── store/
│   │   ├── EventStore.java
│   │   ├── EventStoreRepository.java
│   │   └── StoredEvent.java
│   ├── publisher/
│   │   ├── EventPublisher.java
│   │   └── DomainEventPublisher.java
│   └── domain/
│       ├── ProductCreatedEvent.java
│       ├── ProductUpdatedEvent.java
│       ├── ProductDeletedEvent.java
│       └── DomainEvent.java
├── infrastructure/                  # Infrastructure
│   ├── persistence/
│   │   ├── command/                # Write Database
│   │   │   ├── ProductWriteRepository.java
│   │   │   └── JpaProductRepository.java
│   │   └── query/                  # Read Database
│   │       ├── ProductReadRepository.java
│   │       └── ProductViewRepository.java
│   ├── messaging/
│   │   ├── EventBus.java
│   │   ├── RabbitMQEventBus.java
│   │   └── EventHandler.java
│   └── config/
│       ├── CommandConfig.java
│       ├── QueryConfig.java
│       └── EventConfig.java
└── api/                            # API Layer
    ├── command/
    │   ├── ProductCommandController.java
    │   └── CommandController.java
    ├── query/
    │   ├── ProductQueryController.java
    │   └── QueryController.java
    └── dispatcher/
        ├── CQRSDispatcher.java
        └── MessageDispatcher.java

📋 Diagrama de Clases - Command Side

classDiagram
    class CreateProductCommand {
        +name: String
        +price: BigDecimal
        +category: String
        +description: String
    }
    
    class CreateProductCommandHandler {
        -repository: ProductRepository
        -eventPublisher: EventPublisher
        +handle(command): ProductId
    }
    
    class Product {
        -id: ProductId
        -name: String
        -price: BigDecimal
        -category: String
        +updatePrice(newPrice)
        +updateName(newName)
        +isActive(): boolean
    }
    
    class ProductRepository {
        <<interface>>
        +save(product): void
        +findById(id): Optional~Product~
        +delete(id): void
    }
    
    class EventPublisher {
        +publish(event): void
        +publishAll(events): void
    }
    
    CreateProductCommandHandler --> CreateProductCommand
    CreateProductCommandHandler --> Product
    CreateProductCommandHandler --> ProductRepository
    CreateProductCommandHandler --> EventPublisher

📋 Diagrama de Clases - Query Side

classDiagram
    class GetProductsQuery {
        +category: String
        +page: int
        +size: int
        +sortBy: String
    }
    
    class GetProductsQueryHandler {
        -readRepository: ProductReadRepository
        +handle(query): ProductListView
    }
    
    class ProductView {
        +id: String
        +name: String
        +price: BigDecimal
        +category: String
        +categoryName: String
        +stockQuantity: int
        +averageRating: double
        +reviewCount: int
        +isAvailable: boolean
    }
    
    class ProductReadRepository {
        <<interface>>
        +findByCategory(category, pageable): List~ProductView~
        +searchByName(name, pageable): List~ProductView~
        +findTopRated(limit): List~ProductView~
        +findByPriceRange(min, max): List~ProductView~
    }
    
    class ProductListView {
        +products: List~ProductView~
        +totalCount: long
        +page: int
        +size: int
    }
    
    GetProductsQueryHandler --> GetProductsQuery
    GetProductsQueryHandler --> ProductReadRepository
    GetProductsQueryHandler --> ProductListView
    ProductListView --> ProductView

🔄 Event Flow Diagram

sequenceDiagram
    participant Client
    participant CommandAPI
    participant CommandHandler
    participant WriteDB
    participant EventStore
    participant EventBus
    participant Projection
    participant ReadDB
    participant QueryAPI
    
    Client->>CommandAPI: CreateProductCommand
    CommandAPI->>CommandHandler: handle(command)
    CommandHandler->>WriteDB: save(product)
    CommandHandler->>EventStore: store(event)
    CommandHandler->>EventBus: publish(ProductCreatedEvent)
    EventBus->>Projection: handle(event)
    Projection->>ReadDB: update(productView)
    
    Note over Client: Later...
    Client->>QueryAPI: GetProductsQuery
    QueryAPI->>ReadDB: findByCategory()
    ReadDB-->>QueryAPI: ProductView[]
    QueryAPI-->>Client: ProductListView

🎯 Patrones de Implementación

Command Pattern Integration

classDiagram
    class Command {
        <<interface>>
        +execute(): void
    }
    
    class CommandHandler~T~ {
        <<interface>>
        +handle(command: T): Result
    }
    
    class CommandBus {
        -handlers: Map~Class, CommandHandler~
        +dispatch(command): Result
        +register(commandClass, handler): void
    }
    
    class CreateProductCommand {
        +execute(): void
    }
    
    Command <|.. CreateProductCommand
    CommandBus --> CommandHandler
    CommandHandler <|.. CreateProductCommandHandler

Query Pattern Integration

classDiagram
    class Query {
        <<interface>>
    }
    
    class QueryHandler~T, R~ {
        <<interface>>
        +handle(query: T): R
    }
    
    class QueryBus {
        -handlers: Map~Class, QueryHandler~
        +dispatch(query): Result
        +register(queryClass, handler): void
    }
    
    class GetProductsQuery {
        +category: String
        +page: int
    }
    
    Query <|.. GetProductsQuery
    QueryBus --> QueryHandler
    QueryHandler <|.. GetProductsQueryHandler

📊 Read Model Optimization Strategies

Denormalized Views

graph TB
    subgraph "Normalized Write Model"
        P[Product]
        C[Category]
        R[Review]
        I[Inventory]
    end
    
    subgraph "Denormalized Read Models"
        PV[ProductView]
        PLV[ProductListView]
        PSV[ProductSearchView]
        PCV[ProductCatalogView]
    end
    
    P --> PV
    C --> PV
    R --> PV
    I --> PV
    
    PV --> PLV
    PV --> PSV
    PV --> PCV

Materialized Views

classDiagram
    class ProductSummaryView {
        +productId: String
        +name: String
        +averageRating: double
        +totalReviews: int
        +totalSales: int
        +lastUpdated: LocalDateTime
    }
    
    class ProductCategoryView {
        +categoryId: String
        +categoryName: String
        +productCount: int
        +averagePrice: BigDecimal
        +topProducts: List~String~
    }
    
    class ProductSearchView {
        +productId: String
        +searchableText: String
        +tags: List~String~
        +boost: double
        +isActive: boolean
    }

🔧 Event Sourcing Integration

Event Store Pattern

classDiagram
    class EventStore {
        +saveEvents(streamId, events): void
        +getEvents(streamId): List~Event~
        +getEventsAfter(streamId, version): List~Event~
    }
    
    class StoredEvent {
        +eventId: String
        +streamId: String
        +eventType: String
        +eventData: String
        +eventVersion: int
        +timestamp: LocalDateTime
    }
    
    class EventStream {
        +streamId: String
        +version: int
        +events: List~Event~
        +appendEvent(event): void
    }
    
    EventStore --> StoredEvent
    EventStore --> EventStream

Projection Rebuilding

graph TB
    A[Event Store] --> B[Projection Rebuilder]
    B --> C[Clear Read Models]
    C --> D[Replay All Events]
    D --> E[Rebuild Projections]
    E --> F[Update Read Models]
    F --> G[Validate Consistency]

🎨 Advanced CQRS Patterns

Saga Pattern Integration

classDiagram
    class OrderSaga {
        +handle(OrderCreatedEvent): void
        +handle(PaymentProcessedEvent): void
        +handle(InventoryReservedEvent): void
        +handle(ShippingScheduledEvent): void
    }
    
    class SagaManager {
        +startSaga(sagaType, event): void
        +continueSaga(sagaId, event): void
        +completeSaga(sagaId): void
    }
    
    OrderSaga --> SagaManager

Snapshot Pattern

classDiagram
    class AggregateSnapshot {
        +aggregateId: String
        +version: int
        +data: String
        +timestamp: LocalDateTime
    }
    
    class SnapshotStore {
        +saveSnapshot(snapshot): void
        +getSnapshot(aggregateId): Optional~AggregateSnapshot~
        +getLatestSnapshot(aggregateId): Optional~AggregateSnapshot~
    }
    
    SnapshotStore --> AggregateSnapshot