0

In my appliaction I am using EF. I have the service that can provide some information and clients who want to ask for that information. Let's imagine that I have a User, which has Address, the list of equipments, the list of subscriptions, the list of Balances. Each Balance has the list of Operations.

public class User
{
    public User()
    {
        Equipments = new HashSet<Equipment>();
        Balances = new HashSet<Balance>();
        Subscriptions = new HashSet<Subscription>();
    }

    public long Id { get; set; }
    public string LastName { get; set; }

    public DateTime DOB { get; set; }

    public long AddressId { get; set; }
    public Address Address { get; set; }

    public ICollection<Equipment> Equipments { get; set; }

    public ICollection<Balance> Balances { get; set; }

    public ICollection<Subscription> Subscriptions { get; set; }
}

public class Equipment
{
    public long Id { get; set; }
    public string Model { get; set; }
    public int StateId { get; set; }

    public long UserId { get; set; }

    public User User { get; set; }
}

public class Balance
{
    public Balance()
    {
        Operations = new HashSet<BalanceOperation>();
    }
    public long Id { get; set; }
    public decimal Amount { get; set; }
    public int CurrencyId { get; set; }

    public long UserId { get; set; }

    public User User { get; set; }

    public ICollection<BalanceOperation> Operations { get; set; }
}

public class BalanceOperation
{
    public long Id { get; set; }

    public DateTime OperationDate { get; set; }

    public long BalanceId { get; set; }
    public Balance Balance { get; set; }
}

public class Address
{
    public int Id { get; set; }
    public string City { get; set; }
    public int HouseNumber { get; set; }
}

public class Subscription
{
    public long Id { get; set; }

    public int ServiceId { get; set; }

    public bool IsActive { get; set; }

    public long UserId { get; set; }

    public User User { get; set; }
}

Also I have a DTO UserInfo ,that can contain all that information. Clients want to aks for UserDto at different places of the app. But they don't need all the information all the time. For example, sometimes I will need only user with its Balances without operations. Sometimes - only equipments and subscriptions. Sometimes - balances WITH operations. So, what I want is the client to use one method to get info by providing some kind of includes.

int includes = (int)(IncludeEnum.Adress | IncludeEnum.Subscriptions);
UserInfo userInfo = _service.GetUserInfo(id, includes);

The problem is how to build the architecture of the server, what patterns to use etc. Depends on Includes I will build different queries to DB and fill UserInfo in proper way. The server has to provide info and not to provide not needed info and even not to aks that info in DB. So it would reduce the number of requests. In EF i can use Include() to get related objects. Sometimes I can build queries using LINQ ( from ... join ... select). Or may be all my thoughts are wrong? And I have to do all the things in different way?

1

Your app seems to be very central to the user (or maybe this is just a part of the app), but this is a sign you may have too much in the User class, so break out those lists into other classes.

Example: Balances I don't know about your app, but normally users don't get to directly manage their balances of their accounts. The user possesses very few of the rules managing accounts.

Class Balance Operations {
   GetUserBalances (User) {
   }

}

Call it what you want and return the list. This way you can have logic that drives getting account balances. If you ever need to get balances by something other than user, this is where it would go.

I'd do this for Equipment and create something called Inventory. GetUserEquipment(User). Then if you want to retrieve by Model or User Group, team, company, or whatever, this is where it goes.

It's tempting to have the user class do everything that could possibly involve a user, but at some point (and I think you're there), redesign and move some things into their own classes.

3
  • thank you for your answer. The model I provided was just for an example. In my real project I have different entities and relations. I wanted to demonstrate on simple objects what I need to achieve. My goal is to be able to get the info about some entity with it relations, if the client needs that relations and if he passes the specific value to the request. For example, int includes = (int)(IncludeEnum.Adress | IncludeEnum.Subscriptions); – daewoosh Mar 27 '17 at 16:36
  • the server will analize the include value and do smth like that:context.UserSet.Include("Address").Include("Subscriptions"). So my question is how to build the architecture. What type should I use as an include? is using enums a good idea? How can I parse enum value to the collection of strings, if you understand what I mean. – daewoosh Mar 27 '17 at 16:38
  • I don't know about simple objects, but it doesn't sound like what you're proposing is simple. You could probably build something on your own. – JeffO Mar 27 '17 at 19:58
0

TL;DR Try with all the data first and measure performance.

Is your fear that returning all the user data ("fat user" model) from the database would be too slow? From your description, the work that the DBMS (I am assuming a RDBMS here) will do here is pretty commonplace, and should not be a problem performance-wise.

Certainly using just a single model for Users would lead to much simpler design on the application's side. Using many different models for your Users ("skinny users") complicates also your clients' code. They have to know what type of a DTO they can/should ask for at a given time.

Also, I assume that there are no security issues here (like you have to hide Balances information from some of your clients and Addresses from others).

Note: you may find that the best balance is to split the user model, but only regarding to some aspects. For example, separate Subscriptions to a separate model (named UserSubscription or something) that is related to User, but is-not a User. That will depend on the actual use cases and data amounts.

Recommended course of action:

  1. build a test database, as close to the production specs as possible.
  2. populate it with dummy data. Use a safety multiplier. E.g. your specs are for 100 000 users - create 500 000 dummy users, all with 2 addresses and so on. Remember proper indexing.
  3. Create a SQL query JOINing all the data needed by your application.
  4. Run some queries and take note of the execution times. (If they seem slow, consult with your DBA).
  5. Use your application to run this query. Take note of the times.
  6. If the times are acceptable, use the "fat user" model.
  7. If not, create a similar query for one "skinny user" model - e.g. just user name and addresses.
  8. compare execution times like before.
  9. if this is acceptable, split the User model. If not, you have deeper issues somewhere (network, DBMS configuration, etc.)

Disclaimer: I have not used LinQ, but as far as I know, it should not matter from architecture perspective.

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.