Skip to content

Latest commit

 

History

History
169 lines (144 loc) · 5.71 KB

File metadata and controls

169 lines (144 loc) · 5.71 KB

helidon-mongodb-cqrs

CQRS Java sample using Helidon and MongoDB

Overview

This project demonstrates a CQRS (Command Query Responsibility Segregation) architecture built with:

  • Helidon MicroProfile 3.x — lightweight Java microservices framework

  • MongoDB — document database for persistence

  • Domain-Driven Design (DDD) — aggregates, value objects, domain events, repositories

  • Package-by-feature — each feature is self-contained with its own API, command, query, domain, and infrastructure layers

Architecture

Package-by-feature layout

com.soujava.helidon.cqrs
├── shared/                         ← cross-cutting CQRS abstractions
│   ├── command/
│   │   ├── Command.java            ← marker interface
│   │   ├── CommandBus.java         ← dispatch interface
│   │   ├── CommandHandler.java     ← handler interface
│   │   └── SimpleCommandBus.java   ← in-process registry-based bus
│   ├── query/
│   │   ├── Query.java
│   │   ├── QueryBus.java
│   │   ├── QueryHandler.java
│   │   └── SimpleQueryBus.java
│   └── domain/
│       ├── AggregateRoot.java      ← base class; collects domain events
│       └── DomainEvent.java        ← base class for all domain events
│
└── order/                          ← "Order" bounded context / feature
    ├── api/                        ← REST resources & DTOs (JAX-RS)
    │   ├── OrderResource.java
    │   ├── CreateOrderRequest.java
    │   └── DomainExceptionMapper.java
    ├── command/                    ← write side
    │   ├── CreateOrderCommand.java
    │   ├── CreateOrderCommandHandler.java
    │   ├── ConfirmOrderCommand.java
    │   ├── ConfirmOrderCommandHandler.java
    │   ├── CancelOrderCommand.java
    │   └── CancelOrderCommandHandler.java
    ├── query/                      ← read side
    │   ├── FindOrderByIdQuery.java
    │   ├── FindOrderByIdQueryHandler.java
    │   ├── FindAllOrdersQuery.java
    │   ├── FindAllOrdersQueryHandler.java
    │   └── OrderView.java          ← read model (separate from domain aggregate)
    ├── domain/                     ← DDD domain model
    │   ├── Order.java              ← aggregate root
    │   ├── OrderId.java            ← value object
    │   ├── OrderItem.java          ← entity
    │   ├── OrderStatus.java        ← enum value object
    │   ├── OrderRepository.java    ← repository interface (port)
    │   └── event/
    │       ├── OrderCreatedEvent.java
    │       ├── OrderConfirmedEvent.java
    │       └── OrderCancelledEvent.java
    ├── infrastructure/             ← adapters / persistence
    │   ├── MongoClientProducer.java
    │   └── MongoOrderRepository.java
    └── OrderHandlerRegistrar.java  ← wires handlers into buses at startup

DDD concepts applied

Concept Where

Aggregate Root

Order — all state changes go through it; enforces invariants

Value Object

OrderId, OrderStatus — immutable, identity-less

Entity

OrderItem — has data but lives inside the aggregate boundary

Domain Event

OrderCreatedEvent, OrderConfirmedEvent, OrderCancelledEvent

Repository

OrderRepository interface in the domain; MongoDB implementation in infrastructure

Factory method

Order.create(…​) — the only way to create a new order

CQRS flow

REST Request
     │
     ▼
OrderResource
     │
     ├─[POST /orders]────────► CommandBus.dispatch(CreateOrderCommand)
     │                                  │
     │                                  ▼
     │                        CreateOrderCommandHandler
     │                                  │
     │                                  ▼
     │                        Order.create() → OrderRepository.save()
     │
     └─[GET /orders/{id}]───► QueryBus.dispatch(FindOrderByIdQuery)
                                        │
                                        ▼
                              FindOrderByIdQueryHandler
                                        │
                                        ▼
                              OrderRepository.findById() → OrderView

REST API

Method Path Description

POST

/orders

Place a new order

GET

/orders

List all orders

GET

/orders/{id}

Get a single order by id

PUT

/orders/{id}/confirm

Confirm a pending order

PUT

/orders/{id}/cancel

Cancel an order

Example — create an order

curl -X POST http://localhost:8080/orders \
  -H "Content-Type: application/json" \
  -d '{
    "customerId": "customer-42",
    "items": [
      { "productId": "prod-1", "quantity": 2, "unitPrice": 19.99 },
      { "productId": "prod-2", "quantity": 1, "unitPrice": 5.00  }
    ]
  }'

Configuration

Configuration is provided via src/main/resources/META-INF/microprofile-config.properties:

mongodb.uri=mongodb://localhost:27017
mongodb.database=orders_db
server.port=8080

Running locally

  1. Start MongoDB:

    docker run -d -p 27017:27017 mongo:7
  2. Build and run:

    mvn package
    java -jar target/helidon-mongodb-cqrs-1.0-SNAPSHOT.jar

Running tests

mvn test