There are many things at play here, and by extension a few different approaches to relieving the tension you are describing.
The first discussion to be had is in regard to how you have modeled your domain. Obivously by changing your model, we may be able to provide more idiomatic solution to your problem. So ask yourself this, should Country
be an entity at all? How often does a Country
change, and what effects would such a change cause?
The first solution is to model Country
as a value object. This can be accomplished in two ways: either by treating the current conceptualization as a value entirely or by partitioning your Country
into two (or more) objects according to vectors of change. That is, have one country value object (CountryInfo
) that holds the fields that will never change (e.g. name
), and second country object (Country
) that holds fields that do change (and thus behavior). This would allow any object in your domain to freely hold a reference to your country value object. I like this option the most.
The second discussion is one to which you alluded: separating the read and write models of your application. You are running up against exactly the kind of tension having a shared model can create. That is, you are beginning to consider compromising the design of your model (your business rules) in order to accommodate an orthogonal concern (likely a UI in this case). You didn't directly address this, but I doubt retrieving an ordered list of User
has anything to do with a business rule. Because of this I would hesitate to include such logic in my domain.
With this in mind your second option would be to simply separate your write model from your read model. This can be as simple as allowing any ad-hoc read to be issued as necessary as a read has no chance of corrupting your domain. That is, you don't really need to model anything. Your entire read "model" can just be a database connection that returns arrays.
Your third option, again as you mentioned, is to simply provide for a query in your UserRepository
that handles the semantics and ordering. There will be coupling no matter what you do because the problem you have been given requires that coupling, so I don't see much of an issue here. I find this to be a reasonable compromise for a combined read/write approach. I like this option second best.
And yet a fourth option is to simply store the Country.name
along with it's id
in each User
entity. Again, because this value is unlikely to change, the downsides are few. That said, this option absolutely works against the ideals DDD and clean architecture would have us follow. Should your User
business logic ever become dependent on a field for which it doesn't "own", your system would become susceptible to aberrant behavior. I like this option least.
repo.GetUserPage(pageNo, sortBy)
. Sure, you could say that that pushes some of the business logic to the DB, but if you think about slightly differently, you can reconceptualize it as: your app works with pages, and you are just retrieving pages from storage. In any case, pushing that particular bit of logic to the DB seems low-risk.