Let's start with a brief discussion of what a Domain Service
is, and why you would want to use one. Because I would hesitate, upon an initial inspection, to call your example a Domain Service
.
Domain Services
are most often employed to solve two problems: When you want to extract some sort of calculation or other side-effect-free process into an object of it's own to be used/re-used by many pieces of your domain, or when there exists business logic about the behavior of some process that naturally spans multiple Entities
. The former has many obvious manifestations, but the quintessential example of the latter is for the coordination of transactions that occur between Entities
. That is, the execution of the business process requires the execution of commands to more than one Entity
.
Say, you have an Atm
and a CheckingAccount
. Withdrawing money from the Atm
requires knowledge that the CheckingAccount
has sufficient funds to complete the transaction. Because Atm.WithdrawCurrency( decimal amount )
is completely separate from CheckingAccount.Debit( decimal amount )
but both need to be coordinated to complete the transaction, a Domain Service
is an apt solution. In this case the AtmService
will hold the knowledge regarding whether or not we will be dispensing money.
Aside from the above considerations, Domain Services
will only serve to make your model more anemic. It can be incredibly tempting to button up all of the behavior of your system into discrete "services" that simply act on your model, but that is the antithesis of DDD and only hurts in the long run.
Looking at your example, does it contain business logic (we will get to that Event
in moment)? Not really. Your methods seem to simply be coordinating between your Entities
and the outside world. There aren't really any decisions being made. This reads more like an Application Service
(retrieve piece of model -> execute command -> save).
As for that Event
, I would refrain from raising Events
from an Application Service
. If, for some reason, you need there to be an Event
recording when an AdministrationAccount
is "opened", try to make it more explicit:
// AdministrationAccount
public void Open()
{
this.state = this.state.Open(this);
}
then in it's state:
// ClosedAdministractionAccountState
public IAdministrationAccountState Open( AdministrationAccount account )
{
DomainEvents.RaiseAsync(new AccountRecordOpenedEvent(this));
return new OpenedAdministrationAccountState(account);
}
The example above is using a FSM, but I could conceive of other options here. The important thing is that the logic and Event
is squarely inside your domain where they belong. Not floating out in some Service
method where they will inevitably be bypassed.
As for your other RegisterNewAccount
method. Is this really the right place to create the account? When you talk about a new account being opened in the office, do you say "The AdministrationAccountService
registered a new account with a username
and displayName
"? Or do you say, "An AdminUser
registered a new account"? What I'm getting at here is that the method could easily be refactored to be part of what ever is holding the username
and displayName
before registration. E.g. AdminUser.Register()
. Accounts (and their info) don't come out of nowhere. You can check out this link for a bit more: http://udidahan.com/2009/06/29/dont-create-aggregate-roots/
Refactoring towards the above helps purify and distill your application service.
As for whether or not a Domain Service
can also be a factory? I'd say, generally speaking, no. Is it conceivable? Sure, but you will be better off refactoring your model towards deeper insight such that it isn't necessary. This kind of question is almost always a sign of a deficient model. Not some missing technical detail.