jMolecules DDD

Mar 06, 2026 · 4 min read

software-architecture ddd jmolecules annotations aggregate-root entity value-object repository-pattern domain-events java kotlin

Contents

Overview

jMolecules provides a set of Domain-Driven Design (DDD) annotations to model and structure the domain layer of your application. These annotations help enforce the key building blocks of DDD while making the roles of your entities, aggregates, and other components explicit.

jMolecules DDD Building Blocks — Core annotations for modeling the domain layer

The seven core annotations map directly to tactical DDD patterns:

Annotation Dependency Constraints

DDD Annotation Dependency Matrix
Annotation Must Depend On Must Not Depend On Represents
@AggregateRoot @Entity, @ValueObject Infrastructure or service layers Root of an aggregate, enforces consistency
@Entity @AggregateRoot, @Entity, @ValueObject Infrastructure or service layers Mutable object with identity, part of aggregate
@ValueObject None (pure logic only) Mutable state or infrastructure Immutable object defining a domain concept
@Repository @AggregateRoot Other repositories, services Abstraction for aggregate persistence
@Service Domain objects, Repositories Application or infrastructure logic Stateless domain-specific operations
@Factory @AggregateRoot, @Entity, @ValueObject Infrastructure logic Complex domain object creation
@DomainEvent None (pure event data) Infrastructure logic Event representing domain significance

Annotation Details

@AggregateRoot

The root of an aggregate — the only entry point for manipulating the aggregate's state. All changes within the aggregate must go through the root to ensure consistency.

@Entity

A mutable object with an identity that is part of an aggregate. Represents a piece of the aggregate that can change over time but is controlled by the aggregate root.

@ValueObject

An immutable object that models a concept in the domain. Models values like monetary amounts, coordinates, or dates without tracking identity.

@Repository

Provides an abstraction for persisting and retrieving aggregates. Hides persistence details and serves as the interface for accessing aggregates.

@Service

A stateless service encapsulating domain-specific operations that don't naturally fit within an entity or value object.

@Factory

Used for constructing complex domain objects. Handles creation logic that involves multiple steps or collaborators.

@DomainEvent

Represents a significant occurrence in the domain. Communicates changes to other parts of the system.

Example: E-Commerce Order System

E-Commerce Order System — How DDD building blocks model an order placement flow

AggregateRoot (Order)

@AggregateRoot
class Order(val id: UUID, val customerId: UUID, val items: List<OrderItem>) {

    fun calculateTotal(): Money {
        return items.fold(Money(0.0)) { total, item -> total.add(item.totalPrice) }
    }

    fun placeOrder(): OrderPlacedEvent {
        return OrderPlacedEvent(id, customerId, calculateTotal())
    }
}

Entity (OrderItem)

@Entity
class OrderItem(val productId: UUID, val quantity: Int, val price: Money) {
    val totalPrice: Money
        get() = price.multiply(quantity)
}

ValueObject (Money)

@ValueObject
data class Money(val amount: Double, val currency: String = "USD") {

    fun add(other: Money): Money {
        require(currency == other.currency) { "Currencies must match!" }
        return Money(amount + other.amount, currency)
    }

    fun multiply(multiplier: Int): Money {
        return Money(amount * multiplier, currency)
    }
}

Repository (OrderRepository)

@Repository
interface OrderRepository {
    fun save(order: Order)
    fun findById(id: UUID): Order?
}

Service (OrderService)

@Service
class OrderService(private val orderRepository: OrderRepository) {

    fun placeOrder(order: Order): OrderPlacedEvent {
        val event = order.placeOrder()
        orderRepository.save(order)
        return event
    }
}

DomainEvent (OrderPlacedEvent)

@DomainEvent
data class OrderPlacedEvent(val orderId: UUID, val customerId: UUID, val total: Money)

Why These Rules Matter

Why jMolecules DDD Rules Matter

Encapsulation

Aggregates enforce consistency by restricting direct access to their internal entities and values.

Testability

Domain logic remains decoupled from infrastructure, enabling unit tests without external dependencies.

Domain-Focused Design

The application aligns with the problem domain, improving clarity and maintainability.

Explicit Model

Annotations make roles visible. New developers understand the architecture instantly.

Conclusion

jMolecules DDD annotations provide a structured way to implement Domain-Driven Design principles. By adhering to the constraints, you can maintain a clean separation between your domain logic and infrastructure, ensuring a more robust and scalable system.