Reputation: 121
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)
Account
login validation must occur.LoginAccount
login validation must occur.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
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
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
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