# salahuddin09/hexagonal-kafka-outbox architecture

> Architecture diagram of the GitHub repository salahuddin09/hexagonal-kafka-outbox, generated by GitDiagram from its file tree, README and a sample of source files (last updated 2026-06-27). It is an AI-made overview: check details against the source.

- Interactive diagram: https://gitdiagram.com/salahuddin09/hexagonal-kafka-outbox
- Repository: https://github.com/salahuddin09/hexagonal-kafka-outbox

## Overview

1. This is a Spring Boot hexagonal application centered on an order-processing workflow. The core runtime is Java 25 with Spring Web MVC, Spring Data JPA, Spring Validation, Spring Mail, Flyway, and Apache Kafka. The main infrastructure dependencies are PostgreSQL for state, Kafka for async events, and SMTP/Mailpit for email delivery.

2. The inbound HTTP boundary is under `adapter.in.web`. It exposes REST controllers for users, products, orders, and outbox events. `OrderController` is the primary write entrypoint and returns `202 Accepted` for order creation, emphasizing asynchronous processing rather than immediate completion.

3. The application layer under `application` defines the use-case ports and orchestration services. `CreateOrderService`, `ListOrdersService`, `ListUsersService`, `ListProductsService`, and `ListOutboxEventsService` are the main application services. Ports split the direction of dependency: inbound use cases (`CreateOrderUseCase`, etc.) and outbound persistence/messaging contracts (`OrderPersistencePort`, `OutboxEventPort`, `EmailSenderPort`, and query ports for users/products).

4. The domain layer under `domain` contains the business model and enums. Key aggregates and events include `Order`, `OrderItem`, `User`, `Product`, `OutboxEvent`, `OrderCreatedEvent`, and invoice-related models such as `InvoiceEmail` and `InvoiceEmailOutboxPayload`. Status enums like `OrderStatus` and `OutboxStatus` drive lifecycle transitions.

5. Persistence is isolated in `adapter.out.persistence`. It maps domain objects to JPA entities such as `OrderJpaEntity`, `OrderItemJpaEntity`, `UserJpaEntity`, `ProductJpaEntity`, `OutboxEventJpaEntity`, and `InvoiceEmailJpaEntity`, with Spring Data repositories behind them. `PersistenceMapper` and the adapter classes translate between domain and database shapes, while Flyway migrations define the schema in `src/main/resources/db/migration`.

6. The key reliability mechanism is the outbox table. When an order is created, the application stores the order and an `ORDER_CREATED` record in `outbox_events` within the same transaction. That table also holds retry metadata such as attempt count, status, error message, and timestamps, which makes downstream publishing retryable and observable.

7. `adapter.out.messaging` contains the asynchronous delivery side. `OutboxPublisher` is the scheduled poller that reads pending outbox rows and dispatches them through type-specific publishers. For order events it uses `KafkaOrderEventPublisher`; for invoice-email events it uses `EmailInvoiceOutboxDispatcher` and `SmtpInvoiceEmailSender`.

8. Kafka is used as the first async hop after order creation. `OrderCreatedInvoiceConsumer` in `adapter.in.messaging` consumes the order-created topic and kicks off invoice-email generation. This consumer bridges the Kafka event back into an application use case (`CreateInvoiceEmailFromOrderCreatedEventService`) rather than handling business logic directly.

9. The invoice-email flow is a second outbox-backed stage. The consumer builds invoice content using helpers like `LoadOrderUserService`, `LoadOrderProductsService`, and `InvoiceEmailBodyFactory`, persists an `invoice_emails` record, and emits an `EMAIL_INVOICE_REQUESTED` outbox event. That event is later picked up by `OutboxPublisher`, sent via SMTP, and the order can move to `INVOICE_DELIVERED`.

10. Configuration is centralized in `config`. `KafkaProducerConfiguration`, `MailConfiguration`, `AsyncConfiguration`, `ModelMapperConfiguration`, and `OpenApiConfiguration` wire the infrastructure. Runtime behavior can be toggled with flags such as enabling the invoice consumer or the outbox publisher, which is important for diagramming optional async subsystems.

11. The project also has a small bootstrap/data-seeding concern in `DataFixtureConfiguration`, which inserts default user/product rows only when the tables are empty. That means the application can start with usable catalog data without depending on manual setup.

12. Operationally, the app exposes Swagger/OpenAPI and Actuator health endpoints, but the architectural observability focus is Kafka UI for topic inspection and Mailpit for captured emails. The test suite uses Testcontainers against PostgreSQL and Kafka, so the integration boundary is intentionally exercised against real infrastructure rather than mocks.

Main entry points: [`src/main/java/com/api/sample/application/usecase/CreateOrderService.java`](https://github.com/salahuddin09/hexagonal-kafka-outbox/blob/main/src/main/java/com/api/sample/application/usecase/CreateOrderService.java) (Create order), [`src/main/java/com/api/sample/application/usecase/CreateInvoiceEmailFromOrderCreatedEventService.java`](https://github.com/salahuddin09/hexagonal-kafka-outbox/blob/main/src/main/java/com/api/sample/application/usecase/CreateInvoiceEmailFromOrderCreatedEventService.java) (Invoice flow), [`src/main/java/com/api/sample/application/port/in/CreateOrderUseCase.java`](https://github.com/salahuddin09/hexagonal-kafka-outbox/blob/main/src/main/java/com/api/sample/application/port/in/CreateOrderUseCase.java) (Order ports), [`src/main/java/com/api/sample/adapter/out/messaging/OutboxPublisher.java`](https://github.com/salahuddin09/hexagonal-kafka-outbox/blob/main/src/main/java/com/api/sample/adapter/out/messaging/OutboxPublisher.java) (Outbox poller)

## Diagram (Mermaid)

```mermaid
flowchart TD

subgraph group_group_inbound["Inbound adapters"]
  node_node_order_controller["Order API<br/>REST controller"]
  node_node_catalog_api["Catalog APIs<br/>REST controllers"]
  node_node_kafka_consumer["Invoice consumer<br/>Kafka consumer"]
end

subgraph group_group_application["Application core"]
  node_node_create_order_service["Create order<br/>use case service"]
  node_node_query_services["Query services<br/>read use cases"]
  node_node_invoice_usecase["Invoice flow<br/>use case service"]
  node_node_order_ports["Order ports<br/>inbound/outbound ports"]
  node_node_invoice_ports["Invoice ports<br/>outbound ports"]
end

subgraph group_group_domain["Domain model"]
  node_node_order_model["Orders<br/>domain aggregate<br/>[Order.java]"]
  node_node_outbox_model["Outbox<br/>domain event<br/>[OutboxEvent.java]"]
  node_node_invoice_model["Invoices<br/>domain model<br/>[InvoiceEmail.java]"]
  node_node_catalog_model["Catalog<br/>domain entities<br/>[User.java]"]
end

subgraph group_group_outbound["Outbound adapters"]
  node_node_order_persistence["Order store<br/>persistence adapter"]
  node_node_outbox_persistence["Outbox store<br/>persistence adapter"]
  node_node_invoice_persistence["Invoice store<br/>persistence adapter"]
  node_node_outbox_publisher["Outbox poller<br/>scheduled dispatcher"]
  node_node_kafka_publisher["Kafka publisher<br/>event publisher"]
  node_node_email_dispatcher["Email dispatcher<br/>event dispatcher"]
  node_node_smtp_sender["SMTP sender<br/>mail sender"]
end

subgraph group_group_platform["Platform config"]
  node_node_configuration["App config<br/>Spring configuration"]
  node_node_fixture["Data fixture<br/>bootstrap config"]
end

node_node_order_controller -->|"create order"| node_node_create_order_service
node_node_catalog_api -->|"read data"| node_node_query_services
node_node_kafka_consumer -->|"consume event"| node_node_invoice_usecase
node_node_create_order_service -->|"uses ports"| node_node_order_ports
node_node_invoice_usecase -->|"uses ports"| node_node_invoice_ports
node_node_query_services -->|"read ports"| node_node_order_ports
node_node_order_ports -->|"implemented by"| node_node_order_persistence
node_node_order_ports -->|"implemented by"| node_node_outbox_persistence
node_node_invoice_ports -->|"implemented by"| node_node_invoice_persistence
node_node_create_order_service -->|"creates"| node_node_order_model
node_node_create_order_service -->|"writes outbox"| node_node_outbox_model
node_node_kafka_consumer -->|"async stage"| node_node_outbox_publisher
node_node_outbox_publisher -->|"routes order events"| node_node_kafka_publisher
node_node_outbox_publisher -->|"routes invoice events"| node_node_email_dispatcher
node_node_email_dispatcher -->|"sends mail"| node_node_smtp_sender
node_node_invoice_usecase -->|"builds invoice"| node_node_invoice_model
node_node_invoice_usecase -->|"loads data"| node_node_catalog_model
node_node_configuration -.->|"enables"| node_node_outbox_publisher
node_node_configuration -.->|"enables"| node_node_kafka_consumer
node_node_fixture -->|"seeds"| node_node_catalog_model

click node_node_order_controller "https://github.com/salahuddin09/hexagonal-kafka-outbox/blob/main/src/main/java/com/api/sample/adapter/in/web/OrderController.java"
click node_node_catalog_api "https://github.com/salahuddin09/hexagonal-kafka-outbox/blob/main/src/main/java/com/api/sample/adapter/in/web/UserController.java"
click node_node_kafka_consumer "https://github.com/salahuddin09/hexagonal-kafka-outbox/blob/main/src/main/java/com/api/sample/adapter/in/messaging/OrderCreatedInvoiceConsumer.java"
click node_node_create_order_service "https://github.com/salahuddin09/hexagonal-kafka-outbox/blob/main/src/main/java/com/api/sample/application/usecase/CreateOrderService.java"
click node_node_query_services "https://github.com/salahuddin09/hexagonal-kafka-outbox/blob/main/src/main/java/com/api/sample/application/usecase/ListOrdersService.java"
click node_node_invoice_usecase "https://github.com/salahuddin09/hexagonal-kafka-outbox/blob/main/src/main/java/com/api/sample/application/usecase/CreateInvoiceEmailFromOrderCreatedEventService.java"
click node_node_order_ports "https://github.com/salahuddin09/hexagonal-kafka-outbox/blob/main/src/main/java/com/api/sample/application/port/in/CreateOrderUseCase.java"
click node_node_invoice_ports "https://github.com/salahuddin09/hexagonal-kafka-outbox/blob/main/src/main/java/com/api/sample/application/port/out/InvoiceEmailPort.java"
click node_node_order_model "https://github.com/salahuddin09/hexagonal-kafka-outbox/blob/main/src/main/java/com/api/sample/domain/model/Order.java"
click node_node_outbox_model "https://github.com/salahuddin09/hexagonal-kafka-outbox/blob/main/src/main/java/com/api/sample/domain/model/OutboxEvent.java"
click node_node_invoice_model "https://github.com/salahuddin09/hexagonal-kafka-outbox/blob/main/src/main/java/com/api/sample/domain/model/InvoiceEmail.java"
click node_node_catalog_model "https://github.com/salahuddin09/hexagonal-kafka-outbox/blob/main/src/main/java/com/api/sample/domain/model/User.java"
click node_node_order_persistence "https://github.com/salahuddin09/hexagonal-kafka-outbox/blob/main/src/main/java/com/api/sample/adapter/out/persistence/OrderPersistenceAdapter.java"
click node_node_outbox_persistence "https://github.com/salahuddin09/hexagonal-kafka-outbox/blob/main/src/main/java/com/api/sample/adapter/out/persistence/OutboxEventPersistenceAdapter.java"
click node_node_invoice_persistence "https://github.com/salahuddin09/hexagonal-kafka-outbox/blob/main/src/main/java/com/api/sample/adapter/out/persistence/InvoiceEmailPersistenceAdapter.java"
click node_node_outbox_publisher "https://github.com/salahuddin09/hexagonal-kafka-outbox/blob/main/src/main/java/com/api/sample/adapter/out/messaging/OutboxPublisher.java"
click node_node_kafka_publisher "https://github.com/salahuddin09/hexagonal-kafka-outbox/blob/main/src/main/java/com/api/sample/adapter/out/messaging/KafkaOrderEventPublisher.java"
click node_node_email_dispatcher "https://github.com/salahuddin09/hexagonal-kafka-outbox/blob/main/src/main/java/com/api/sample/adapter/out/messaging/EmailInvoiceOutboxDispatcher.java"
click node_node_smtp_sender "https://github.com/salahuddin09/hexagonal-kafka-outbox/blob/main/src/main/java/com/api/sample/adapter/out/messaging/SmtpInvoiceEmailSender.java"
click node_node_configuration "https://github.com/salahuddin09/hexagonal-kafka-outbox/blob/main/src/main/java/com/api/sample/config/KafkaProducerConfiguration.java"
click node_node_fixture "https://github.com/salahuddin09/hexagonal-kafka-outbox/blob/main/src/main/java/com/api/sample/adapter/out/persistence/DataFixtureConfiguration.java"

classDef toneNeutral fill:#f8fafc,stroke:#334155,stroke-width:1.5px,color:#0f172a
classDef toneBlue fill:#dbeafe,stroke:#2563eb,stroke-width:1.5px,color:#172554
classDef toneAmber fill:#fef3c7,stroke:#d97706,stroke-width:1.5px,color:#78350f
classDef toneMint fill:#dcfce7,stroke:#16a34a,stroke-width:1.5px,color:#14532d
classDef toneRose fill:#ffe4e6,stroke:#e11d48,stroke-width:1.5px,color:#881337
classDef toneIndigo fill:#e0e7ff,stroke:#4f46e5,stroke-width:1.5px,color:#312e81
classDef toneTeal fill:#ccfbf1,stroke:#0f766e,stroke-width:1.5px,color:#134e4a
class node_node_order_controller,node_node_catalog_api,node_node_kafka_consumer toneBlue
class node_node_create_order_service,node_node_query_services,node_node_invoice_usecase,node_node_order_ports,node_node_invoice_ports toneAmber
class node_node_order_model,node_node_outbox_model,node_node_invoice_model,node_node_catalog_model toneMint
class node_node_order_persistence,node_node_outbox_persistence,node_node_invoice_persistence,node_node_outbox_publisher,node_node_kafka_publisher,node_node_email_dispatcher,node_node_smtp_sender toneRose
class node_node_configuration,node_node_fixture toneIndigo
```

## Components

### Inbound adapters

HTTP and Kafka entrypoints into the application

- [Order API](https://github.com/salahuddin09/hexagonal-kafka-outbox/blob/main/src/main/java/com/api/sample/adapter/in/web/OrderController.java): REST controller · Creates orders asynchronously and exposes the main write path · `src/main/java/com/api/sample/adapter/in/web/OrderController.java`
- [Catalog APIs](https://github.com/salahuddin09/hexagonal-kafka-outbox/blob/main/src/main/java/com/api/sample/adapter/in/web/UserController.java): REST controllers · Read endpoints for users, products, and outbox events · `src/main/java/com/api/sample/adapter/in/web/UserController.java`
- [Invoice consumer](https://github.com/salahuddin09/hexagonal-kafka-outbox/blob/main/src/main/java/com/api/sample/adapter/in/messaging/OrderCreatedInvoiceConsumer.java): Kafka consumer · Consumes order-created events and starts invoice email generation · `src/main/java/com/api/sample/adapter/in/messaging/OrderCreatedInvoiceConsumer.java`

### Application core

Use cases, orchestration services, and ports

- [Create order](https://github.com/salahuddin09/hexagonal-kafka-outbox/blob/main/src/main/java/com/api/sample/application/usecase/CreateOrderService.java): use case service · Coordinates order creation and outbox recording · `src/main/java/com/api/sample/application/usecase/CreateOrderService.java`
- [Query services](https://github.com/salahuddin09/hexagonal-kafka-outbox/blob/main/src/main/java/com/api/sample/application/usecase/ListOrdersService.java): read use cases · Lists orders, users, products, and outbox events · `src/main/java/com/api/sample/application/usecase/ListOrdersService.java`
- [Invoice flow](https://github.com/salahuddin09/hexagonal-kafka-outbox/blob/main/src/main/java/com/api/sample/application/usecase/CreateInvoiceEmailFromOrderCreatedEventService.java): use case service · Builds invoice email content from order-created events · `src/main/java/com/api/sample/application/usecase/CreateInvoiceEmailFromOrderCreatedEventService.java`
- [Order ports](https://github.com/salahuddin09/hexagonal-kafka-outbox/blob/main/src/main/java/com/api/sample/application/port/in/CreateOrderUseCase.java): inbound/outbound ports · Use-case and persistence contracts for orders, outbox, and catalog queries · `src/main/java/com/api/sample/application/port/in/CreateOrderUseCase.java`
- [Invoice ports](https://github.com/salahuddin09/hexagonal-kafka-outbox/blob/main/src/main/java/com/api/sample/application/port/out/InvoiceEmailPort.java): outbound ports · Contracts for invoice email persistence and delivery · `src/main/java/com/api/sample/application/port/out/InvoiceEmailPort.java`

### Domain model

Business entities, events, and lifecycle enums

- [Orders](https://github.com/salahuddin09/hexagonal-kafka-outbox/blob/main/src/main/java/com/api/sample/domain/model/Order.java): domain aggregate · Order, order item, status, and order-created event model · `src/main/java/com/api/sample/domain/model/Order.java`
- [Outbox](https://github.com/salahuddin09/hexagonal-kafka-outbox/blob/main/src/main/java/com/api/sample/domain/model/OutboxEvent.java): domain event · Outbox entries, types, statuses, and retry metadata · `src/main/java/com/api/sample/domain/model/OutboxEvent.java`
- [Invoices](https://github.com/salahuddin09/hexagonal-kafka-outbox/blob/main/src/main/java/com/api/sample/domain/model/InvoiceEmail.java): domain model · Invoice email payload and invoice email aggregate · `src/main/java/com/api/sample/domain/model/InvoiceEmail.java`
- [Catalog](https://github.com/salahuddin09/hexagonal-kafka-outbox/blob/main/src/main/java/com/api/sample/domain/model/User.java): domain entities · Users and products used by order and invoice workflows · `src/main/java/com/api/sample/domain/model/User.java`

### Outbound adapters

Persistence and messaging implementations

- [Order store](https://github.com/salahuddin09/hexagonal-kafka-outbox/blob/main/src/main/java/com/api/sample/adapter/out/persistence/OrderPersistenceAdapter.java): persistence adapter · Maps orders and catalog entities to JPA repositories · `src/main/java/com/api/sample/adapter/out/persistence/OrderPersistenceAdapter.java`
- [Outbox store](https://github.com/salahuddin09/hexagonal-kafka-outbox/blob/main/src/main/java/com/api/sample/adapter/out/persistence/OutboxEventPersistenceAdapter.java): persistence adapter · Persists and queries outbox events with retry state · `src/main/java/com/api/sample/adapter/out/persistence/OutboxEventPersistenceAdapter.java`
- [Invoice store](https://github.com/salahuddin09/hexagonal-kafka-outbox/blob/main/src/main/java/com/api/sample/adapter/out/persistence/InvoiceEmailPersistenceAdapter.java): persistence adapter · Persists invoice emails for the second async stage · `src/main/java/com/api/sample/adapter/out/persistence/InvoiceEmailPersistenceAdapter.java`
- [Outbox poller](https://github.com/salahuddin09/hexagonal-kafka-outbox/blob/main/src/main/java/com/api/sample/adapter/out/messaging/OutboxPublisher.java): scheduled dispatcher · Reads pending outbox rows and routes them to type-specific publishers · `src/main/java/com/api/sample/adapter/out/messaging/OutboxPublisher.java`
- [Kafka publisher](https://github.com/salahuddin09/hexagonal-kafka-outbox/blob/main/src/main/java/com/api/sample/adapter/out/messaging/KafkaOrderEventPublisher.java): event publisher · Publishes order-created events to Kafka · `src/main/java/com/api/sample/adapter/out/messaging/KafkaOrderEventPublisher.java`
- [Email dispatcher](https://github.com/salahuddin09/hexagonal-kafka-outbox/blob/main/src/main/java/com/api/sample/adapter/out/messaging/EmailInvoiceOutboxDispatcher.java): event dispatcher · Delivers invoice-email outbox events through SMTP · `src/main/java/com/api/sample/adapter/out/messaging/EmailInvoiceOutboxDispatcher.java`
- [SMTP sender](https://github.com/salahuddin09/hexagonal-kafka-outbox/blob/main/src/main/java/com/api/sample/adapter/out/messaging/SmtpInvoiceEmailSender.java): mail sender · Sends invoice emails to the configured mail server · `src/main/java/com/api/sample/adapter/out/messaging/SmtpInvoiceEmailSender.java`

### Platform config

Spring wiring, bootstrap, and runtime integration

- [App config](https://github.com/salahuddin09/hexagonal-kafka-outbox/blob/main/src/main/java/com/api/sample/config/KafkaProducerConfiguration.java): Spring configuration · Kafka, mail, async, mapper, and OpenAPI wiring · `src/main/java/com/api/sample/config/KafkaProducerConfiguration.java`
- [Data fixture](https://github.com/salahuddin09/hexagonal-kafka-outbox/blob/main/src/main/java/com/api/sample/adapter/out/persistence/DataFixtureConfiguration.java): bootstrap config · Seeds default users and products when tables are empty · `src/main/java/com/api/sample/adapter/out/persistence/DataFixtureConfiguration.java`

## Connections

- Order API → Create order: create order · REST write request enters the create-order use case
- Catalog APIs → Query services: read data · User, product, and outbox endpoints delegate to query services
- Invoice consumer → Invoice flow: consume event · Kafka message triggers invoice-email orchestration
- Create order → Order ports: uses ports · Order creation depends on inbound use case and outbound persistence contracts
- Invoice flow → Invoice ports: uses ports · Invoice flow persists invoice emails and accesses supporting data through ports
- Query services → Order ports: read ports · Query services rely on persistence/query ports
- Order ports → Order store: implemented by · Persistence adapter fulfills order and catalog persistence contracts
- Order ports → Outbox store: implemented by · Outbox persistence adapter fulfills outbox contract
- Invoice ports → Invoice store: implemented by · Invoice persistence adapter stores invoice-email records
- Create order → Orders: creates · Order use case produces order aggregate and order-created event
- Create order → Outbox: writes outbox · Order creation stores an order-created outbox record in the same transaction
- Invoice consumer → Outbox poller: async stage · Invoice generation is followed by outbox-driven delivery
- Outbox poller → Kafka publisher: routes order events · Poller dispatches order-created outbox records to Kafka
- Outbox poller → Email dispatcher: routes invoice events · Poller dispatches invoice-email outbox records to email delivery
- Email dispatcher → SMTP sender: sends mail · Dispatcher hands invoice delivery to the SMTP sender
- Invoice flow → Invoices: builds invoice · Invoice use case produces invoice email content and payloads
- Invoice flow → Catalog: loads data · Invoice flow loads user and product details for the email body
- App config → Outbox poller: enables · Runtime flags can turn the poller on or off
- App config → Invoice consumer: enables · Runtime flags can turn the invoice consumer on or off
- Data fixture → Catalog: seeds · Bootstrap configuration inserts default catalog rows
