chytonpide
chytonpide

Reputation: 121

DDD: Can I pass a Domain-Service as a parameter of aggregate constructor

I have a domain that maintains accounts for other systems(Media).
At first, I derived the following aggregate root

public class Account extends Entity {
    
    private AccountId accountId;
    private TenantId tenantId;
    private LoginAccount loginAccount;
    private Media media;
    private LoginValidity validity;
    
    
    public Account(TenatId shopId, Media media, LoginAccount loginAccount) {
        this.accountId = new AccountId(); 
        this.setTenatId(shopId);
        this.set(media);
        this.setLoginValidity(LoginValidity.NOT_REQUESTED);
    }
    
    
    public void validateLogin(LoginValidationService loginValidationService) {
        LoginValidity validity = loginValidationService.validateLoginFor(media,loginAccount);
        setLoginValidity(validity);
        
        //TO-DO EVENT PUBLISHING
    }
    
    public void changeLoginAccount(LoginAccount loginAccount) {
        setLoginAccount(loginAccount);
        setLoginValidity(LoginValidity.NOT_REQUESTED);
        
        //TO-DO EVENT PUBLISHING?
    }

    ...Setters
}

And I also derived the LoginValidationService as a Domain-Service.
LoginValidationService determines strategy(policy) using Media

And then I also derive two business logic(invariant)

My question is that for the first invariant,LoginValidationService(A Domain-Service) could be a parameter for aggregate root's constructor like this

public class AccountApplicationService {
   private LoginValidationService loginValidationService;
   private AccountRepository accountRepository;

   public Account createAccount(CreateAccountCommand command) {
        TenantId tenantId = new TenantId(command.getTenantId());
        Media media = mediaService.mediaFrom(command.getMediaId());
        
        Account account = new Account(tenantId, 
                                     media,
                                     command.getLoginAccount(),
                                     loginValidationService);
        
        accountRepository.save(account);
        return ccount;
   }
   ...
}
public class Account extends Entity {
    
    private AccountId accountId;
    private TenantId tenantId;
    private LoginAccount loginAccount;
    private Media media;
    private LoginValidity validity;
    
    public Account(TenatId shopId, 
                   Media media, 
                   LoginAccount 
                   loginAccount,LoginValidationService loginValidationService) {

        this.accountId = new AccountId(); 
        this.setTenatId(shopId);
        this.set(media);
        LoginValidity validity =
                 loginValidationService.validateLoginFor(media,loginAccount);
        this.setLoginValidity(validity);
    }
   .... 
}

Is exist the Pattern above? (passing the domain-service to the constructor) and is it the right approach of DDD?

Do I have to derive the first invariant as a use-case? like this,

public class AccountApplicationService {
   private LoginValidationService loginValidationService;
   private AccountRepository accountRepository;

    public Account createAccountAndValidateLogin(CreateAccountAndValidateLoginCommand command) {
        TenantId tenantId = new TenantId(command.getTenantId());
        Media media = mediaService.mediaFrom(command.getMediaId());
        
        MediaAccount mediaAccount = new MediaAccount(tenantId,media,command.getLoginAccount());
        mediaAccount.validateLogin(loginValidationService);
        
        mediaAccountRepository.save(mediaAccount);
    }
   ...
}

Please give me any advice.

-----------Edit---------
I add the Account's constructor code. LoginValidationService is not a member of Account.

Upvotes: 2

Views: 1212

Answers (3)

chytonpide
chytonpide

Reputation: 121

I found an additional invariant for Account creating that Account can't hold itself so I derived AccountProvisionService(Domain-Service). The Application-Service code is as follows.

...

public Account createAccount(CreateAccountCommand command) {
        
        return AccountProvisioningService.provisionAccount(
             command.getTenantId(), command.getMediaId(), command.getLoginAccount());
    }


public void changeLoginAccount(ChangeLoginAccountCommand command) {
        Account account = existingAccount(command.getShopId(),command.getAccountId());
        LoginValidity loginValidity = loginValidationService.validateLoginFor(Account.media(), command.getLoginAccount());
        account.changeLoginAccount(command.getLoginAccount(),loginValidity);
        
        AccountRepository.save(account);
    }
...

Upvotes: 0

ismaelcabanas
ismaelcabanas

Reputation: 104

Would be a solution for you to pass the LoginValidity as Account aggregate constructor parameter?

public Account createAccount(CreateAccountCommand command) {
        TenantId tenantId = new TenantId(command.getTenantId());
        Media media = mediaService.mediaFrom(command.getMediaId());
        LoginValidity loginValidity = 
            loginValidationService.validateLoginFor(media,command.getLoginAccount());

        Account account = new Account(tenantId, 
                                     media,
                                     command.getLoginAccount(),
                                     loginValidity);
        
        accountRepository.save(account);
        return ccount;
}

I think that to validate an account is something that the entity cannot do by yourself so it is clear is domain service's responsibility, then I would see in the use case flow logic, or you can create the account with a domain service responsible of validate and create the account and you had the business logic encapsulates in domain service instead of use case.

Upvotes: 1

VoiceOfUnreason
VoiceOfUnreason

Reputation: 57249

Can I pass a Domain-Service as a parameter of aggregate constructor

Yes, but I would expect that to make your life harder in the long run.

An AGGREGATE is a cluster of associated objects that we treat as a unit for the purpose of data changes. -- Evans, 2003

Domain Services, however, don't change -- they are stateless, and "any client can use any instance of a particular SERVICE without regard to the instance's individual history."

So mixing the patterns in this way is a bit odd on two fronts; including the service as a member of the aggregate suggests that it changes, and also it implies that this aggregate has a special relationship with a particular instance of the service, which contradicts the notion that they are not interchangeable.

It's not clear to me what compensating advantage you get in a design where the service is embedded within your aggregate.

Upvotes: 0

Related Questions