Skip to content

Architecture Overview

Máté Károlyi edited this page Sep 23, 2025 · 4 revisions

🏗 Architecture Overview

This project follows Hexagonal Architecture (Ports & Adapters) combined with Domain-Driven Design (DDD).
The goal is to keep the business logic independent from frameworks, databases, or external services.

📐 High-level architecture:

Architecture


🔹 Layers

1️⃣ Domain Layer

  • 💡 Pure business logic.
  • Contains:
    • Aggregates (Order, Basket, Customer, Payment, Product)
    • Domain Events (OrderCreated, OrderPaid, etc.)
    • Business rules and invariants.
  • Framework-agnostic → no Spring annotations, no persistence logic.

2️⃣ Application Layer

  • ⚙️ Orchestrates use cases.
  • Coordinates domain objects and invokes ports to reach external systems.
  • Contains:
    • Application services (e.g., OrderCommandService, BasketCommandService, NotificationService)
    • Use case interfaces (OrderCreation, BasketCheckout, NotificationSending)
    • Transaction boundaries, logging, validation.
  • Publishes domain events into the Outbox for messaging.

3️⃣ Adapters Layer

  • 🔌 Bridges external technologies with the domain.
  • Two directions:
    • Inbound Adapters → expose the app to the outside world (e.g., REST controllers, message listeners).
    • Outbound Adapters → implement ports for persistence, messaging, notifications, payments, etc.
  • Examples:
    • REST adapter (Spring REST controllers)
    • JPA adapter (PostgreSQL)
    • Stripe payment adapter
    • Email/SMS notification adapters
    • RabbitMQ messaging adapter

4️⃣ Infrastructure Layer

  • 🛠 Cross-cutting technical concerns.

🔹 Ports & Adapters in Practice

  • Ports → Java interfaces defined in the Domain or Application layer (OrderRepository, Notificator, PaymentProcessor).
  • Adapters → Implementations of those ports in the Adapters layer.
  • Dependency Rule → Dependencies always point inward:

🔹 Example Flow: Creating an Order

1️⃣ Inbound Adapter (REST controller) receives POST /orders.
2️⃣ Application Service (OrderCommandService) executes the use case.
3️⃣ Domain Layer creates a new Order aggregate and raises OrderCreated event.
4️⃣ Application Service persists the order through OrderRepository (a port).
5️⃣ Outbound Adapter (JPA) saves order into PostgreSQL.
6️⃣ Outbox Adapter stores the OrderCreated event in the outbox table (serialized payload).
7️⃣ Messaging Adapter (RabbitMQ) periodically polls the outbox, reads new events, and publishes them to the message broker.
8️⃣ Notification Adapter (e.g., email) or other bounded contexts consume the event and act on it.

📡 Messaging flow in action:
Messaging flow


🔹 Why This Matters

  • Testable → Business rules can be tested without infrastructure.
  • Maintainable → Clear separation of concerns.
  • Replaceable → Adapters (e.g., In-Memory → JPA, or Email → SMS) can be swapped without touching domain logic.
  • 📦 Scalable → Outbox + Messaging ensures reliable event-driven workflows.

Clone this wiki locally