Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can Domain Services access Repositories?

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)
    }
}
like image 484
mgryszko Avatar asked Nov 14 '14 12:11

mgryszko


People also ask

Is repository domain or data layer?

Repository interface belongs to the domain layer since it plays the roles of defining the operations on Entity required for implementing business logic (Service). Implements the methods defined in Repository interface. Implements CRUD operations of the Entity and is dependent on persistence layer.

What are domain services?

Domain Services stores centralized directory information and lets users and domains communicate. When a user attempts to connect to a device or resource on a network, this service provides login authentication, verifying the user's login credentials and access permissions.

What are domain services DDD?

Domain Services (or just Services in DDD) is used to perform domain operations and business rules. In his DDD book, Eric Evans describes a good Service in three characteristics: The operation relates to a domain concept that is not a natural part of an Entity or Value Object.

What is DDD example?

An aggregate is a domain-driven design pattern. It's a cluster of domain objects (e.g. entity, value object), treated as one single unit. A car is a good example. It consists of wheels, lights and an engine.


1 Answers

Well, I would say that if choosing which entities to load comes down to a good deal of domain logic, then I might delegate that task to the domain service. However, I would usually strive to resolve aggregate root references in application services.

However, I think you might have a few other issues in here or at least you could use some other DDD tactical patterns like Domain Events to improve your design.

In my opinion, you shouldn't have any notification sending code in the application service at all. Instead, a MoneyTransferred domain event could be raised by the domain service. You would then have a subscriber to this event which would be in charge for sending the email.

In addition to decoupling your components, you are enriching the ubiquitous language of your domain. Sending a notification now occurs in response to a money transfer being made rather than as part of the same process and many other interested parties could react as well.

Finally, your domain service is currently violating the rule of modifying only one aggregate root per transaction. I'm not saying you can never break the rule, but usually that's a good indicator that you should be using eventual consistency, or perhaps that your aggregate boundaries are wrong.

If you think about it, money transfers between accounts rarely occurs in an atomic way (if they ever do). I guess that could be the case if the two accounts are in the same bank, but eventual consistency has to be used when the transfer spans multiple banks.

like image 52
plalx Avatar answered Sep 21 '22 17:09

plalx