8

I'm trying to learn better software design and recently found the Repository and Service Layer patterns. From my understanding, the repository basically contains the data access code and the service layer calls the repository to get that data and then performs some logic and processing to that data.

So far from reading up on these there is, generally, not a set series of methods that the repository has. However, the repository usually has methods along these lines:

  • List/Get/Read/etc.
  • Create
  • Save
  • Update
  • Delete

I'm trying to understand the naming conventions for repositories. What should I call the "List/Get/Read/etc." method? I'll give an example.

I'm currently working on a project that will read from a bunch of directories and files. These files represent sensor readings that are being generated by a completely separate and already existing system.

Should method names be specific to that particular type of repository or go for more generic sounding names? Such as:

Generic names:

interface ISensorRepository
{
    IEnumerable<Sensor> GetAll(); /  IEnumerable<Sensor> ListAll(); / etc.
    Sensor GetByID(int id);
}

Entity specific names:

interface ISensorRepository
{
    IEnumerable<Sensor> GetAllSensors(); / IEnumerable<Sensor> ListAllSensors(); / etc.
    Sensor GetSensorByID(int id);
}
user9993
  • 5,833
  • 11
  • 56
  • 117

3 Answers3

7

I'll go with OP's first option the "generic naming"

Why? its to avoid smurf naming convention (it's a programming jargon), its what you do when you keep repeating yourself.

You already named the type of entity the repository represents, why go through all the work repeating yourself? If you stick with Single Responsibility Principle, you don't have to worry about your abstraction being leaky.

The following is what I try to avoid:

Employee
- EmployeeID
- EmployeeFirstName
- EmployeeLastName
- EmployeeAddress
- EmployeeNumber
- EmployeeAge
- EmployeeEmail
- EmployeeHiringDate
- EmployeeThis
- EmployeeThat
- EmployeeBlabla
- Employee...
- Employee...

Its kind of crazy right?

Yorro
  • 11,445
  • 4
  • 37
  • 47
5

Martin Fowler in POEAA defined Repository as a mediator between domain and data mapping layers and that it acts like an in-memory domain object collection.

As it should act like an in memory collection, I like naming my methods as follows.

public interface IRepository<T> where T : class {
    T Create(T entity);

    void Create(IList<T> entities);

    T Update(T entity);

    T FirstOrDefault(Expression<Func<T, bool>> clause);

    IEnumerable<T> Where(Expression<Func<T, bool>> clause);

    IEnumerable<TResult> Select<TResult>(Expression<Func<T, TResult>> selector);

    T First();

    IEnumerable<T> All();
}

See http://martinfowler.com/eaaCatalog/repository.html for a short discussion around the pattern

3dd
  • 2,520
  • 13
  • 20
  • 3
    Avoid returning a `List`. What happens if the consumer adds something to the list? Does that cause database updates? What about two different consumers adding or removing from the lists at the same time? Do they affect each other? I wouldn't know what to expect. Use the most generic type that fits your needs, which in this case is `IEnumerable` or `IQueryable`. – JounceCracklePop Aug 08 '15 at 23:08
  • @CarlLeth agreed 100% – 3dd Aug 08 '15 at 23:42
  • 5
    Do not create generic repositories. That's bad design. – Phill Aug 09 '15 at 07:50
  • @Phill care to elaborate why? – 3dd Aug 09 '15 at 07:52
  • 3
    Because you make assumptions about the domain. You force implementations of Get/All/Update etc when the domain may not require those. That's bad design. – Phill Aug 09 '15 at 07:56
  • @Phill - You design generic repositories, then build domain specific repositories on top of that. You'll get reusability. However you are correct that using generic repositories in the domain is bad design as stated. – Yorro Aug 09 '15 at 09:02
  • @Yorro you design generic repositories, the domain specific ones inherit the generic contract which forces implementation that may not be valid for the domain specific repository. Beyond having an empty interface so you can register all based on IRepository but register based on the first implemented interface, is ok. As long as IRepository is empty. – Phill Aug 09 '15 at 09:06
  • @Phill The domain specific repository will inherit the generic repository - Yes, but you will only expose the correct abstraction in the domain through the domain specific interface. It's a bit of extra engineering. My point is explained thoroughly by the accepted answer in this link http://stackoverflow.com/questions/1230571/advantage-of-creating-a-generic-repository-vs-specific-repository-for-each-obje – Yorro Aug 09 '15 at 09:18
  • My domain sits behind a service layer, this layer won't expose any domain objects that should not have been exposed. The reuse I get from using the generic repository negates the negatives from the leaky abstraction. But I do agree that you end up with methods that don't make sense in all cases – 3dd Aug 09 '15 at 09:39
  • All interesting points. A lot of these sensors are read only so there would not be a need for Update and Create etc, but in some cases there will be. – user9993 Aug 09 '15 at 11:21
  • @Phill, can you explain if your domain uses a mix of the generic methods? For example let's say half of you domain accepts inserts and the other half only accepts updates. Would you create an IRepository that all repositories implement, an IInsertRepository, and an IUpdateRepository? I am curious if this is your argument. – DDiVita Aug 12 '15 at 20:32
  • @DDiVita - well to begin with, I don't use repositories at all, its an unnecessary abstraction when you're using a framework that abstracts ADO.NET. But if I was to use repositories, I would only implement an abstract base repository which has some protected helpers, and then domain specific interfaces, and register all that implement the base, to the interface. Then all the repositories implement only the functionality they require. – Phill Aug 14 '15 at 18:40
  • @Phill, I disagree that the abstraction is unnecessary. What if you decided to plug in NHibernate or another ORM. I personally had a project where we went from NHIbernate to EF and without that abstraction it would have been a nightmare. You would have to rewrite all of your underlying DAL for your application. That seems unnecessary to me. – DDiVita Aug 16 '15 at 13:56
0

Consider what repository variables are likely to be named by the consumer. I would write something like:

public void DoSomethingWithSensors(ISensorRepository sensors)
{
    //Which looks better?
    foreach (var sensor in sensors.All)
    //Or:
    foreach (var sensor in sensors.GetAllSensors())
}

I generally like to avoid redundancy whenever possible. People aren't likely to forget which repository they're calling All on, so I'd name it that, and use similar generic names for other operations. This also gives you the advantage that you can abstract the basic interface into one generic type:

public interface IRepository<T>
{
    IQueryable<T> All { get; }
    void Update(T entity);
    T Get(int id); //If you have a standard type for IDs
    //etc.
 }

I would avoid methods like (not to pick on 3dd's nice answer):

T FirstOrDefault(Expression<Func<T, bool>> clause);
List<TResult> Select<TResult>(Expression<Func<T, TResult>> selector);

If you go down this route, you may find you're soon declaring all of LINQ in your interface. Just expose one IQueryable<T> and rely on LINQ to provide all those options. I'd stick to IQueryable over IEnumerable if you're using EntityFramework.

Another consideration is that this pattern is likely to break the Interface Segregation Principle. A lot of your consumers will only grab IRepository instances to query, never to write. Whether or not the added complexity of breaking it into two interfaces is worth addressing this problem depends on the expected lifecycle of your software and whether lots of other systems are likely to depend on your code.

JounceCracklePop
  • 339
  • 3
  • 11
  • Thank you for your answer it raises some good points. Could you elaborate on the Interface Segregation Principle and how the pattern could break this? Do you mean I should have two interfaces, one for reading the data, and the other for writing the data? – user9993 Aug 09 '15 at 06:03
  • 1
    Do not create generic repositories. That's bad design. – Phill Aug 09 '15 at 07:51
  • The interface segregation principle says that if I consume your interface (e.g. ask for it as a method parameter), it should logically follow that I will use every feature that interface exposes. If I just want one feature, but your interface has three, then my code is forced to "depend" on those extra two features. If you change the Create logic and it affects `IRepository`, every consumer that used the interface just for reading needs to recompile. It's not a big deal unless you're designing a framework, but you did say that some sensors are read-only, so consider it. – JounceCracklePop Aug 09 '15 at 19:44