-1

I have a requirement to list country codes (e.g. CA, US) by username. A single user may have zero, one or more countries and a single username. A country has a single country code.

I have a UserRepository and a CountryRepository.

My question is - which repository should I be putting this code into?

Is there a generally accepted standard? Or is it more of an 'either works, pick one and stick with it' kind of thing?

Regarding the content of the repositories, they're largely just CRUDs. CountryRepo just has two methods - GetCountryByCode and ListCountries. UserRepository has ListUsers, GetUserByUsername, DeleteUserByUsername, UpdateUser, CreateUser.

Sarov
  • 403
  • 3
  • 13
  • 1
    We do not know what is in those repositories, what more you have or how they are related to your application(s). There is nothing to answer here. – Martin Maat Mar 19 '20 at 14:09
  • @MartinMaat I didn't realize that was relevant. I've updated my Question. – Sarov Mar 19 '20 at 14:23
  • 1
    Your use of the work repository is very confusing. You seem to mean class when you write repository. You should check dependencies. Country codes are tied to countries in a 1-to-1 manner. Users may change country, country codes will never change country. So country codes belong to the country class. – Martin Maat Mar 19 '20 at 14:35
  • @MartinMaat Well, yes, a repository is a class. I'm writing in C#; everything is a class. Users actually have multiple countries. My requirement is to get the codes of all the countries of a given user. – Sarov Mar 19 '20 at 14:37
  • @MartinMaat He asked where should he place the "get country code by username" computer code, not where should he place the country code information. – Ted Chirvasiu Mar 19 '20 at 16:31
  • @Sarov I don't think there's a standard way to do it since the user does not own the country entities nor does the country own the user entities. It seems to be a query more related to the user than the countries ("which country codes does this user have?"), so I'd put it in the UsersRepository or if the countries of an user are always eagerly loaded inside the User object, I'd make a method inside the User class called GetCountryCodes() which would loop through the in-memory list of the user's countries and extract the codes from them. – Ted Chirvasiu Mar 19 '20 at 16:39

3 Answers3

0

It can't possibly be in CountryRepository, as that knows nothing about users. Whereas, I would expect the country code(s) to be closely associated with each user. So UserRepository is the only oen that couple provide the method.

Simon B
  • 9,167
  • 4
  • 26
  • 33
0

Any answer to this question is going to be subjective... There's no "right answer".

Here's how I would make that determination for myself:

What information am I retrieving? That's what repository the code should be in.

In your example, you are retrieving Country Codes, so the query code should be in the Country repository.

Now, the query parameter just happens to be a UserId, but you may have many queries in your Country repository that return Country-related data based on a variety of query parameters. This just happens to be one example. You are still returning Country data, so it should be in the Country repository.

Eric King
  • 10,876
  • 3
  • 41
  • 55
0

The question addresses the exact reason as to why I don't like repositories as the sole interface of your persistence layer. It leads to a lot of categorization which sometimes ends up as a completely subjective judgment.

This is a problem of your own making. You decided to use repositories which categorizes all your needed data operations and queries, and are now struggling to properly categorize some of your data operations and queries.

The solution is simply to come up with a better categorization, one that can actually house all the data operations and queries you need it to, in a logical and consistent manner.


The rest of this answer is based on an earlier answer I've written which touches on the same topic.


Repositories, at least the basic implementation thereof, tend to take a "one entity type per repository" approach. If you want to get a list of all countries with all their provinces and all of the province's cities, you'll have to separately talk to the CountryRepository, ProviceRepository and CityRepository.
In short, repositories limit you to only being able to execute single-entity-type queries. For the same example, you would have to launch 3 separate database queries in order to get all countries and their provinces and their cities.

And don't get me wrong, I like repositories. I like having the neat little boxes so you can separate your storage of different domain objects, which e.g. would allow you to get the countries from your database but the provinces from a remote API and the cities for a second remote API.

But this separation of entity types into their own private boxes very much clashes with the benefit of having relational databases, where part of the benefit is that you can launch a single query that can take related entities into account (for filtering, sorting or returning).

You might rightly respond that "a repository can still return more than one entity type". And you would be correct. But if you have a query which returns both Foo and Bar entities, where do you place it? In the FooRepository? In the BarRepository? There may be examples where the choice is easy, but there are also examples where the choice is hard and multiple developers may have different categorization methods and thus the codebase becomes inconsistent and the true purpose of the "one entity type per repository" approach will be thrown out the window.


Query objects are the only real way to get around the "one entity type per repository" approach. The shortest way I can describe what a query object is, is that you should think of it as a "one method repository".

This changes the responsiblity of the class, i.e. serving a particular query instead of serving all CRUD actions of a given entity, but it doesn't change the technical implementation (a query object's query method is effectively the same as a repository query method).

Repositories suffer from having to deal with multiple types of entities, and the more methods a repository has, the more distinct entity types it's likely going to be handling. By separating each repository method into a query object of its own, you've simply remove the contradictory suggestion that "this repository only handles one entity type", and instead are suggesting that "this query object runs this particular query, regardless of which entity types it needs to use".

Note: it's called a command object instead of a query object if it's about a data operation instead of a query, but the explanation and implementation is the same for both.

This tends to lead to a CQRS pattern, but you're not forced to migrate your entire codebase to such an approach. Simply splitting your repositories into smaller command/query objects can be sufficient for your use case.


You can still use query objects and repositories at the same time, and you are then able to enforce that repositories will never handle more than their designated entity type.

If a query makes use of more than one entity type (e.g. Country and Province), then it belongs in its own private query object (e.g. CountriesAndTheirProvincesQuery). If a query only focuses on one entity type (e.g. Country), then it belongs to that entity type's repository (e.g. CountryRepository).

Generally speaking, simple CRUD actions still belong to a repository, but complex (i.e. multi-entity) queries (and data operations) need their own query (or command) object.

Flater
  • 44,596
  • 8
  • 88
  • 122