When implementing the repository pattern for my ASP.NET project, I encountered some problems on which I can't get my head around. So I have a few questions on how to implement the repository pattern the right way.
From my experience I think that having classes/models without behaviour only in my application, next to their repositories is not good OOP. But, this was the way I implemented the repository pattern. I just make everywhere I need an repository instance, to perform some actions. The result of this approach was that all my domain classes didn't have behaviour.
They were just objects holding data with no methods. My teacher said to me that I was using thin models and that I should strive to make fat models. In response to that feedback, I implemented some business logic in the classes, but I ran into some problems:
Scenario:
My User
class had a list of Friends with User objects in it, to represent the friends of a certain user. When adding a new friend to an user the domain class method checks if the "friend" already exists in the friends list. If not, he will be added to the list. These changes need to be sent to the database for persistence.
I know this has to be done in the repository for each domain class, but where do the calls to the repository methods belong in the architecture of the application?
Right now I call the repository methods in the domain class methods itself, to persist changes to the database:
public void AddFriend(User friend)
{
foreach(User f in Friends)
{
if(f.Username == friend.Username)
{
throw new Exception(String.Format("{0} is already a friend.", friend.Username));
}
}
Friends.Add(friend);
userRepo.AddFriend(this.Id, friend.Id);
}
Is this a good approach, because of some reason I think it's not. Regarding to unit testing, we need with this approach some dependancy injection, which says to me that it is not a independent class (good testable unit). I have read some posts from people saying they use an extra service layer or something. I think these kind of abstraction is needed here, but how does a certain service layer look like? What is in it, which methods etc.?
I have seen some other students making use of static methods in a domain class which provides functionality like adding a new object, updating an object, deleting an object, and getting all objects.
Example:
public class Tram
{
private static TramRepository Repo = new TramRepository(new DBTram());
public static void AddTram(int tramID, TramType type, int lineNr)
{
Tram tram = new Tram(tramID, type, TramStatus.depot, lineNr, true, null);
Repo.AddTram(tram);
}
public static List<Tram> GetAll()
{
Repo.GetAll();
}
}
I find it a weird thing to have a method adding a new entity to the database in a domain class, which is that entity itself. Also for the GetAll() method, I think it's weird to have a method in a class itself that gets all trams. So a tram object can get all trams. I think this is a weird way of implementing the repository pattern. Am I right?
So, what kind of abstraction is needed here? Does there have to be an extra layer? If so, how does this layer look like? (Example code) Or am I searching in the wrong direction and is there another solution that counters the problem of unit testing with the repository pattern?
This architecture problem I encounter every time, made sure I need it to be answered.
I find it hard to explain this problem clearly, but I hope you guys understand it.
The repository pattern is an abstraction layer between your database and business layer. The idea behind implementing repository pattern is to hide the data persistence details.
This post (which is the third in a series about the data layer) aims to explain why it’s still a great choice. A Repository mediates between the domain and data mapping layers, acting like an in-memory domain object collection. Client objects construct query specifications declaratively and submit them to Repository for satisfaction.
YOU SAY:”The reason is that if a controller needs some data from the database, it can simply use the repository interfaces to get the data.” QUESTION: In your YouTube video on the Repository pattern, you say that the repositories and the Unit of Work are in the same assembly.
” a repository is like a collection of domain objects. ” yeah thats right and it should only return domain objects.I agree with that but I have a question “Mapping is not the responsibility of the repository.” but what if my domain entity is different that my data model entity?? who and where I have to make the mapping to persist the data.
Your questions are absolutely normal, but don't expect to find an absolute answer. Welcome to the software industry!
Here is my opinion:
- Is it good OOP to have an application that relies on an architecture that, next to their repositories, only has models/classes that hold values with no behaviour?
I think you try to implement a repository pattern, but you miss a higher architecture view. Most apps are at least decoupled in 3 layers: View (Presentation), Business and DataAccess. The repository patterns takes place in the DataAccess, this is where you can find pure data object. But this data access layer is used by a business layer, where you will find a domain model, classes with business behavior AND data. The unit tests effort must be on domain model in the business layer. These tests should not care about how the data are stored.
- Where do I call repository methods in the architecture of the application?
Again no absolute answer, but usually it makes sense to use Something like a business service. These services could arrange the flow between different domain object, and load, and save them in repositories. This is basically what you do in your AddFriend class, and it belongs into a business layer.
Regarding to unit testing, we need with this approach some dependancy injection, which says to me that it is not a independant class
Business services are ususally dependant on Repositories, it is a really common case for unit testing. The Tram class can hold business behavior and is still independent. The AddTram business service need a repository, and dependency injection allows to test it anyway.
- Methods that insert, update and delete new entities in a database, are they supposed to be in a class itself?
For this one I can be clear and loud: please don't do that, and yes, CRUD operations belong to the Tram Repository. It's certainly not a business logic. That's why in your example you need two classes in two different layers:
"Because i have seen some other students making use of static methods in a domain class which provides these functionality"
Use of static methods is clearly not a good idea for that, it means anybody could store data through your object, even though it's supposed to handle business case. And again, data storage is not a business case, it's a technical necessity.
Now to be fair, all these concepts need to be discuss in context to make sense, and need to be adapted on each new project. This is why ou job is both hard and exciting: context is king.
Also I wrote a blog article centered on MVVM, but I think it can help to understand my answer.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With