Application layering
@ApplicationService and @DomainService are provided by spring-ddd-annotations and define the transaction conventions for the application and domain layers.
Both are independent of the persistence strategy — they work whether you use event sourcing, JPA aggregates, or a combination.
Application service
@ApplicationService marks a class as the entry point for a use case.
It owns the transaction boundary for application-layer work: reading state, building commands, and interpreting results.
The default propagation is REQUIRED — it joins an existing transaction if one is active, otherwise starts a new one.
import de.dwittkoetter.ddd.annotation.ApplicationService
import de.dwittkoetter.ddd.cqrs.command.CommandGateway
@ApplicationService
class AccountApplicationService(private val commandGateway: CommandGateway) {
fun openAccount(accountId: AccountId, initialBalance: Money) {
commandGateway.send(OpenAccount(accountId, initialBalance))
}
fun deposit(accountId: AccountId, amount: Money) {
commandGateway.send(DepositMoney(accountId, amount))
}
}
A bean that does nothing but dispatch a single command rarely needs @ApplicationService — each command handler already runs in its own transaction.
Reach for it when a use case reads state, coordinates more than one command, or interprets results within a single transaction boundary.
|
Domain service
@DomainService marks a class that encapsulates domain logic spanning more than one aggregate.
The default propagation is MANDATORY — a domain service must be called within an existing transaction and never starts its own.
import de.dwittkoetter.ddd.annotation.DomainService
@DomainService
class TransferService(
private val accounts: BankAccountRepository,
) {
fun transfer(sourceId: AccountId, targetId: AccountId, amount: Money) {
val source = accounts.findById(sourceId).orElseThrow()
val target = accounts.findById(targetId).orElseThrow()
source.withdraw(amount)
target.deposit(amount)
accounts.save(source)
accounts.save(target)
}
}
The caller — typically an @ApplicationService — is responsible for opening the transaction before invoking a domain service.