Should my Domain Exceptions be thrown from Application Layer?
I try to avoid domain exceptions as much as I can and prefer to make invalid states unreachable instead. The first reason is that exceptions are for exceptional, unexpected things, the second that I don't like my code to be cluttered with fine-grained try/catches for every little business-ish thing that could go wrong.
BacklogItemNotFoundException
To me this is typically your Repository or query service returning null or an empty list. No need for a domain exception.
EmailPatternBrokenException
TooManyCharactersForNameException
I let the validation feature of my web framework handle these. You could also check it in the Domain but it will rarely reach that point and you don't really need to handle that kind of error specifically.
As a result, the two typical scenarios are:
+-----------------------+--------------------+-------------------------------------------------+
| Domain | Application | Presentation |
+-----------------------+--------------------+-------------------------------------------------+
| Expected failure case | Return Result.Fail | Clean error message |
+-----------------------+--------------------+-------------------------------------------------+
| Exception | - | Caught in catch-all clause > 500 error or other |
+-----------------------+--------------------+-------------------------------------------------+
The question is a contradiction. If it is a Domain Exception, it means that it is thrown by the domain.
Anyway, exceptions thrown by the domain should be handled by the application layer.
I have an exception handler decorator for the command bus, that catch any domain exception and translates it into an Application Exception.
This application exception is thrown to the adapters.
Adapters know about application exceptions, not domain exceptions.
UPDATE
My domain exception is an abstract base class from which the concrte domain exceptions inherit
public abstract class DomainException extends RuntimeException {
private static final long serialVersionUID = 1L;
private ErrorMessage mainErrorMessage;
private List<ErrorMessage> detailErrorMessages;
protected DomainException ( List<ErrorMessage> aDetailMessages, Object... aMainMessageArgs ) {
this.mainErrorMessage = new ErrorMessage(this.getClass().getSimpleName(), aMainMessageArgs );
this.detailErrorMessages = ( (aDetailMessages==null) ? new ArrayList<ErrorMessage>() : aDetailMessages );
}
public ErrorMessage mainErrorMessage() {
return this.mainErrorMessage;
}
public List<ErrorMessage> detailErrorMessages() {
return this.detailErrorMessages;
}
}
ErrorMessage has a key and a list of args. The messages are in a property file where the key is the name of the concrete domain exception class.
Application exception is just one type, which holds the concrete text message.
public class ApplicationException extends Exception {
private static final long serialVersionUID = 1L;
private String mainMessage;
private String[] detailMessages = new String[0];
public ApplicationException ( String aMainMessage, Throwable aCause, String... aDetailMessages ) {
super ("Main Message = "+aMainMessage+" - DetailMessages = "+Utils.toString(aDetailMessages), aCause );
this.mainMessage = aMainMessage;
this.detailMessages = ( (aDetailMessages==null) ? (new String[0]) : aDetailMessages );
}
public String mainMessage() {
return this.mainMessage;
}
public boolean hasDetailMessages() {
return (this.detailMessages.length > 0);
}
public String[] detailMessages() {
return this.detailMessages;
}
}
I have a decorator (wraps the execution of every command) for handling domain exceptions:
public class DomainExceptionHandlerDecorator extends Decorator {
private final DomainExceptionHandler domainExceptionHandler;
public DomainExceptionHandlerDecorator (DomainExceptionHandler domainExceptionHandler) {
this.domainExceptionHandler = domainExceptionHandler;
}
@Override
public <C extends Command> void decorateCommand(Mediator mediator, C command) throws ApplicationException {
try {
mediator.executeCommand(command);
} catch ( DomainException de ) {
this.domainExceptionHandler.handle (de);
}
}
}
And I have a domain exception handler that takes a domain exception, translates it into an app exception by reading properties file (TextMessageService does the job) and throw the app exception.
public class TranslatorDomainExceptionHandler implements DomainExceptionHandler {
private final TextMessageService configurationService;
public TranslatorDomainExceptionHandler ( TextMessageService aConfigurationService ) {
this.configurationService = aConfigurationService;
}
@Override
public void handle ( DomainException de ) throws ApplicationException {
ErrorMessage mainErrorMessage = de.mainErrorMessage();
List<ErrorMessage> detailErrorMessages = de.detailErrorMessages();
String mainMessage = this.configurationService.mensajeDeError ( mainErrorMessage );
String[] detailMessages = new String [ detailErrorMessages.size() ];
int i = 0;
for ( ErrorMessage aDetailErrorMessage : detailErrorMessages ) {
detailMessages[i] = this.configurationService.mensajeDeError ( aDetailErrorMessage );
i++;
}
throw new ApplicationException ( mainMessage, de, detailMessages);
}
}
The adapter (an UI for example) will catch the app exception and show its message to the user. But it doesn't know about domain exceptions.
I will add my 2 cents about error handling, not specifically related to DDD.
The exception are part of the contract you expose to the consumer. If you're expected to for example add an item to a shopping cart, the exception you may explicitly throw include itemNotAvailable, shoppingCartNotExisting, etc...
Technical exception on the other hand are not part of the contract, they may occurs but shouldn't be explicitly handled as no one can do anything about it, they must imply the operation interruption (and the rollback of the current unit of work).
A rest interface is a contract for an operation on a resource. When using rest over http the terms of the contract are related to the http protocol.
Typical operation described above (adding ie. post an item on a cart resource) would be translated to, for example, 404 for shoppingCartNotExisting and 409 for itemNotAvailable (conflict ie. the update on the resource is no more possible because some state has changed meantime).
So yes all "domain" exception (expected exceptions as part of the contract) should be explicitly mapped by the rest adapter, all unchecked ones should result in a 500 error.