Can Domain Services access Repositories? Or they should work on Aggregates/Entities passed to them by Application Services?
Consider two code samples of the same business operation - money transfer. As first step, I alter account balances. Then I obtain the notification email and send the notification. I know, I should probably abstract the way notifications are sent (email, SMS, carrier pigeon), but for simplicity's sake let's assume that we support only emails by now.
Variant 1 uses repositories inside the domain service. Variant 2 resolves dependencies in the application service and passes them to the TransferDomainService
.
In this example the operation is simple (subtract money from one account and add it to another). But if there would be more business rules involved (possible requiring access to more aggregates)? If variant 2 is applied, then the application service must have the knowledge what exactly the domain service requires. If variant 1 is chosen, then the domain service asks repositories for what it requires to perform its task.
(Notes about snippets: Groovy code to strip verbosity of Java. DDD building blocks included in names)
Variant 1
class TransferApplicationService {
def transferDomainService
def customerDomainService
def emailNotifierInfrastructureService
def transfer(fromAccount, toAccount, amount) {
transferDomainService.transfer(fromAccount, toAccount, amount)
def email = customerDomainService.accountNotificationEmail(toAccount)
emailNotifierInfrastructureService.notifyAboutTransfer(email, amount)
}
}
class TransferDomainService {
def accountRepository
def transfer(fromAccount, toAccount, amount) {
def from = accountRepository.findByNumber(fromAccount)
def to = accountRepository.findByNumber(toAccount)
to.decreaseBalance(amount)
from.increaseBalance(amount)
}
}
Variant 2
class TransferApplicationService {
def accountRepository
def transferDomainService
def customerDomainService
def notifierInfrastructureService
def transfer(fromAccount, toAccount, amount) {
def from = accountRepository.findByNumber(fromAccount)
def to = accountRepository.findByNumber(toAccount)
transferDomainService.transfer(from, to, amount)
def email = customerDomainService.accountNotificationEmail(toAccount)
notifierInfrastructureService.notifyAboutTransfer(email, amount)
}
}
class TransferDomainService {
def transfer(fromAccount, toAccount, amount) {
to.decreaseBalance(amount)
from.increaseBalance(amount)
}
}