-2

We have the current software pattern below.

Sql Server Db --> Repository --> Domain Layer---> Dto Layer

The Dto layer filters sensitive data, and brings only required domain data to client API users.

A repository pulls All the data from Sql Server database. One thing I don't understand with Repository is, it may get extra includes extra items, (Customer Type, Customeraddress), when certain APIs do not require them. And selects generally All the columns, when user may not require this.

So by using the Repository pattern, it can ruin the performance, and cause more work to be queried than needed. How do Software architects account of justify for this? Trying to learn.

Example Repository

public Task<List<Tra>> GetCustomerDataByCustomerId(string customerId)
{

    var customerData = db.Customer
                  .Include(c=>c.CustomerType)
                  .Include(c=>c.CustomerAddress)
                  .Where(c=>c.CustomerId == customerId);
4

A repository pulls All the data from Sql Server database. One thing I don't understand with Repository is, it may get extra includes extra items when certain APIs do not require them.

I've concluded some things from your problem:

  • Your repository always includes additional data
  • You only sometimes need additional data to be returned
  • You do not want additional data to be returned when it is not needed, which reveals that you prioritize runtime performance over ease of development (there's nothing wrong with that, but it's important to acknowledge your priorities so you can justify the necessity of certain implementations)

The logical solution is to change your repository so you can dynamically control whether certain data should be included or not.

So by using the Repository pattern, it can ruin the performance, and cause more work to be queried than needed.

No, a half-implemented repository which lacks performance-improving features is ruining performance. That's not an argument against the repository design pattern, it's an indication that you need to improve your implementation of that design pattern.

How do Software architects account of justify for this?

If my car has a flat tire, should I then boycott the manufacturer? Should I try to justify why I buy a car from them? Or should I replace the tire?

How you implement that improvement is up to you. Depending on the scale and complexity of this logic, you can use a simple method parameter:

public Foo[] GetFoosByName(string name, bool includeBars = false)
{
    IQueryable<Foo> query = db.Foos;

    if(includeBars)
        query = query.Include(f => f.Bar);

    return query.Where(f => f.Name == name).ToList();
}

You could also implement that same switch on the constructor level:

public class FooRepository
{
    private readonly bool includeBars;

    public FooRepository(bool includeBars)
    {
        this.includeBars = includeBars;
    }

    private IQueryable<Foo> GetQuery()
    {
        IQueryable<Foo> query = db.Foos;

        if(includeBars)
            query = query.Include(f => f.Bar);

        return query;
    }

    public Foo[] GetFoosByName(string name)
    {
        return GetQuery().Where(f => f.Name == name).ToList();
    }
}

Or if you want fine-grained control over exactly which related entities to load, you could come up with a list of types to include, e.g.:

public enum RelatedEntityTypes { Bar, Baz, Bat }

private IQueryable<Foo> GetQuery(RelatedEntityTypes[] includeTypes)
{
    IQueryable<Foo> query = db.Foos;

    foreach(var includeType in includeTypes)
    {
        switch(includeType)
        {
            case RelatedEntityTypes.Bar:
                query = query.Include(f => f.Bar); break;
            case RelatedEntityTypes.Baz:
                query = query.Include(f => f.Baz); break;
            case RelatedEntityTypes.Bat:
                query = query.Include(f => f.BaT); break;
        }
    }

    return query;
}

public Foo[] GetFoosByName(string name, RelatedEntityTypes[] includeTypes)
{
    return GetQuery(includeTypes).Where(f => f.Name == name).ToList();
}

This same example can also again be moved to the constructor.

Or you can create completely different repositories, each of which have their own hardcoded includes.

The world is your oyster here. Just implement a solution that is appropriate for what you need.

| improve this answer | |
  • hi Flater, this is an interesting pattern using booleans or include types, I've read hundeds of articles on generic repository, never heard of this parametized pattern, can you cite resource, or did you create it at previous companies? Anyways, its very nice, thanks – AlanSmith5482 Jun 29 at 4:32
0

There isn't a simple solution.

There is trade-off between complexity of the repository API and performance.

You can always make the repository methods more specific, so they return only required data. Or you can pass in list of includes. But at that point, it becomes a question if you even need a middleman between the ORM and your code.

There really isn't a best solution and different people have different opinions on which solution is preferable.

| improve this answer | |
  • yeah, I wasn't really looking for a solution, just more on How software architects justify this, I'm not saying this is wrong either, interested in hearing their viewpoints Why Tradeoff balance necessary – AlanSmith5482 Jun 26 at 6:18
  • 2
    @AlanSmith5482: There's nothing to justify, it's a matter of developing what you need. No design pattern is going to be able to guarantee that it covers all of your requirements for you. That is your job as a developer. – Flater Jun 26 at 11:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.