0

I've seen various uses of the repository pattern. I'm leaning toward a pattern that I see far less frequently, and I'm wondering if there is a good reason for that.

Pattern 1: Access the Repo through the business object by injecting into the Constructor

        Class Teacher : IPerson
        {
            Private IRepository myRepository;

            Internal Teacher(IRepostory repo){
                This.myRepository = repo;
            }

            Public overrides void Save(){
                Repo.Save(this);
            }
        }

example:

 IPerson p = DataAccess.GetPersonFromId(id);
 p.Name = "Bill";
 p.Save();

Benefits

  1. The constructor will be internal and access only by a factory pattern, so I'm not worried about the complexity here.
  2. IPerson forces an implementation of Save() method but teacher does not need to know how it's being persisted
  3. Works similar to an Entity Framework Proxy object
  4. I can call Save() on an Iperson object without needing to know its a Teacher
  5. Application -> Business Object -> Repository seems like the logical dependency structure.

Cons

  1. The business objects are not Plain Old C# objects anymore.

  2. Any changes to the entity repository are likely going to need to change the "Person" interface.

  3. Should I be applying the same pattern with an IFactory? Do we keep injecting services?

Pattern 2: Access Directly

        IPerson p = DataAccess.GetPersonFromId(id);
        IRepostory repo = DataAccess.GetRepositority()
        p.Name = "Bill";
        repo.Save(p);

Benefits

  1. Seems like the simpler way to do things.

Cons

  1. I can't very well make use of a generic repository that can be used for all derived types. I would like to use an Irepository interface that can take a Person Type and know how to persist it.

Summary

I was leaning toward pattern 1, but almost every example I see out there uses a version of Pattern 2.

The main goal is that I don't want to be using TeacherRepository anywhere in the Application Layer. I would like to rely entirely on IRepository. However, It doesn't look like I can do that in Pattern 2, because Save() needs to know its dealing with a Teacher to properly persist the data.

Are there any unique other patterns that could allow me to work with a generic repository?

mfreedm52
  • 161
  • 10

1 Answers1

0

About pattern 1: It comes in contradiction with the principle of keeping the domain object unaware not only of the underlying storage type, but also of the simple fact that it has to be stored somewhere. More of it, by applying pattern 1 you are breaking the Single Responsibility Principle: you'll have two reasons to change the domain object: business logic and storage. And, from the behavioural perspective, to be saved is not the responsibility of the Teacher but of a repository. So, imho the pros and cons don't need to be discussed anymore. Note: not all frameworks follow a good OOP design. Think about the ORMs using, in the most cases, Active Record. They are using something like "class Teacher extends ActiveRecord". Or impose conditions like "one class per table". So, what do you think?

About pattern 2 and your question: here you are trying to enforce what I mentioned above: to somehow standardize the data access workflow in a way that will make possible to store any type of domain object properties and constructs. Object relational mapping... You can do this, but you'll have to deal with a lot of cons. And you'll have to think your business matters in terms of business logic PLUS storage structure. Not to mention the lack of flexibility when you'll have to deal with queries having a higher grade of complexity. In the end - I've heard - you'll even find out that some of the tasks are actually impossible to be solved.

So, my recommendation is to keep the "Plain Old" business objects completely unaware of storage, to define specific repositories for them and to "bind them together" from outside (like through services).

Actually, a repository is a layer of data access abstraction on top of the so-called data mapper. The data mapper - only - communicates with the underlying database. In the context of your question you can find here a good argumentation:

And here are some other resources:

Good luck.

  • You mentioned: "_by applying pattern 1 you are breaking the Single Responsibility Principle: you'll have two reasons to change the domain object: business logic and storage. And, from the behavioural perspective, to be saved is not the responsibility of the Teacher but of a repository"_ **I would argue that the Teacher object is still persistance ignorant as to how it is persisted, and the class would not need to change if you changed _how_ it was stored. It would only change if the interface changed, in that case it's unlikely you can avoid refactoring** – mfreedm52 Oct 05 '17 at 15:35
  • @mfreedm52 Indeed, the `Teacher` and all other domain objects are unaware of how the persistence takes place. But, let's say, in one moment you decide to rename repo's `Save()` to `Store()`. It's a normal change, right? Now, should this change be made in a domain object? Does changes regarding storage - changes of any kind, I mean - belong to some other data types other than the ones dedicated to provide data access abstraction? –  Oct 05 '17 at 17:43
  • @mfreedm52 From a SRP point of view, I found recently this great short podcast episode: [SOLID principles with Uncle Bob](https://www.hanselman.com/blog/HanselminutesPodcast145SOLIDPrinciplesWithUncleBobRobertCMartin.aspx). In it, Mr. Martin describes SRP from a very simple and clear perspective. Also, the most voted (w. 3) answer from [this question](https://stackoverflow.com/questions/46541197/does-the-single-responsibility-principle-work-in-oop) I liked a lot. –  Oct 05 '17 at 17:51
  • The change to a public method of an API is almost always going to have a ripple effect and cause a lot of classes to change. It would cause the same change to pattern 2. It actually seems more likely that less classes would be effected in pattern 1, because only domain objects need changing. – mfreedm52 Oct 06 '17 at 17:27
  • from my research, there are a lot more people that agree with you, and I'll admit something about pattern 1 doesn't feel right to me, but it does seem to accomplish everything I am trying to do. Thank you for the podcast recommendation, I'm a fan of Uncle Bob's work – mfreedm52 Oct 06 '17 at 17:29
  • @mfreedm52 Indeed, any such change causes a ripple effect, independently of the class types. But the question is: _in which classes_ are the changes to be made? And, of course, it can be argued about the number of affected classes. So, what I was trying to point out, is that the SRP is the _necessary and sufficient argument_ to not give multiple responsibilities to classes, e.g. to not give multiple reasons to change to classes, even though it might seem easier to manage some problem aspects by doing it. –  Oct 06 '17 at 18:56
  • @mfreedm52 You know, actually it could be good SO questions the following, too: _Why is not Pattern 1 better then Pattern 2?_, or _Why is not good Pattern 1?_ ;-) Then you would also receive responses with use-cases which would evidentiate the practical aspects. As for the podcast: You are very welcome! –  Oct 06 '17 at 18:59