I have a Product
entity with a Category
value object (contrived example).
There's an API endpoint /products?category=Keyboards,Mice
The classes at play here are the:
ListProductsController
(interfaces layer)ProductsRetriever
(application layer)ProductRepository
(domain / infrastructure layer)Product
(entity / aggregate root)Category
(value object)
Within the domain model, categories are modelled as a list of objects.
I'm a bit confused with regards to the repository, however. Should it have a method:
public List<Product> findByCategoriesIn(List<Category> categories)
or
public List<Product> findByCategoriesIn(List<String> categories)
If it should be queried using a ValueObject (i.e. Category
) who should convert the list of strings from the API request into the list of categories?
Should it happen within the controller or should the application service take care of the transformation?
Best Answer
Not your fault.
It may help to review chapter six of the blue book. Evans writes
In other words, the REPOSITORY api is expressed in the domain specific language, and the domain agnostic concerns of "how do I get this data to/from the database" are enclosed within the implementation.
From this, you can infer that the consumer of the repository API should be speaking in domain specific values, not domain agnostic primitives.
This is consistent with a theme that Evans comes back to repeatedly; that you want your developers to be spending most of their time in the domain specific language, not manipulating primitive data types.
So the capability for this conversion will normally be provided by the domain model, in the form of value object constructors, factories, message builders, and so on. These tools will normally be invoked by the application code, before everything moves into the domain specific view.
That said... there's been a sort of push back against that approach. It's really kind of weird to start from a string, and now we have to convert it into a MumbleId, that gets passed to a repository that will convert it back into a string for use as a query parameter, and then some document/recordset/bytes are returned, and we reconstitute that into a domain model, that we then turn around and convert into a json document that we send back over the wire as bytes.
CQRS
is, in part, an answer to the question: can't we just bypass the entire model and copy the bytes that we need? Yes, sometimes that makes a lot of sense.