Search code examples
domain-driven-designcqrsrequesthandler

DDD / CQRS - Does a request handler (or controller) can throw exceptions defined at the domain level?


Good morning,

Let's say, we've a domain defining an exception such as ObjectNotFoundException which expect an identifier (VO), defined at the domain model.

Question

Can we throw domain exceptions from the request handlers directly, for instance:

    class ObjectRequestHandler implements RequestHandler
    {
        ...

        public function __invoke(Request $request, Response $response)
        {
            // Will self-validate and throw an exception if not a valid UUID
            $objectId = ObjectId::fromString(strval($request->param('object_id'])));

            $object = $this->repository->find((string)$objectId);

            if (NULL === $object) {
                // Exception defined at the domain level...
                throw new ObjectNotFoundException($objectId);
            }

            ...
        }
}

Doing this also lead to usage of the identifier VO in the request handler... It MUST be also noted that the throwed exception will be catched by the default exception handler which in turn, will prepare and send a JSON response.

Finally, note that the request handler here, is an implementation detail, not part of the question. Please don't comment about it.

Thank you.


Solution

  • Your example shows the correct usage of the repository to fetch an object from the data store based on an identifier.

    Let's unpack and expand the workflow a little more to fit the paradigms of DDD, to help answer the question:

    • API Controller (or Request Handler) would invoke an Application Service with request params sent by the callee.
    • Request params forwarded to the Application Service can be simple data (like JSON) or can be objects (like DTOs)
    • Application Service has access to the correct repository associated with the object.
    • Repositories are outside the domain layer
    • Application Service would load the objects into memory using these repositories before handing over the control to (or invoking a method in) the domain layer.
    • The ObjectNotFound error is thrown typically from the repository if no object is found for the given identifier
    • The domain layer typically receives all objects it needs to work on from the Application Service or builds objects using factory methods.
    • The actual process is all about assigning or transforming attribute values according to business rules while ensuring invariant rules are satisfied. So the kind of errors that Domain Layer throws is Business Rule Errors (or Validation Errors).

    So,

    • ObjectNotFoundException is not a Domain Exception
    • You are not at the domain level yet, so calling the identifier as a ValueObject is incorrect

    Ignoring Application Services for a moment, you are spot on in your usage of the concept. The code example is correct in structure. It's just the terms that need to be clarified.