1

I'm working on client-server application. Client architecture (class hierarchy) looks like this:

  • System (name is of course different, more descriptive) - represents whole system
    • Module A - a class instance representing one module. One module = group of similar data and operations
    • User Module - another module
      • Users - class instance which represents collection of users, allows adding, removing, reading, etc.
      • Groups - represents user groups
    • etc.

Architecture above represents the system, data, operations. Handles client requests and server responses. Caches queries if required. But it does NOT handle GUI, for that I use separated code. The most of the gui uses view for displaying data which is based on simple logic: one view (class) type represents a view of one (single) item type. For example UserView displays data about user (from User instance).

Often the item represents a collection of items. For example there might be item like UserEvent which contains some name (of the event), date and list of users. However, the list of users is implemented as list of IDs (UserId), nothing more. So now UserEventView takes this list and wants to create instances of UserView in order to display more detailed information about users (for example as list of name). So now the UserEventView needs to access Users to translate UserId to User in order to provide UserView correct data. So, what's the best way for the UserEventView to access Users?

I can see multiple ways how to solve it, but none seems to be ideal:

  • Singleton - the most simple and easiest way. Either of System (even though UserEvenView doesn't care about it) or for every single module.
  • Users instance is given to UserEventView by it's parent, which is provided by it's parent, which is provided by it's parent, ...
  • There is UsersProvider component attached to the UserView which searches for GUI root where it searches for a component which provides System instance and obtains Users from it.

Notes:

  • If you want to suggest some exception in design for users, then imagine I'm working with cars, shoes, anything.
  • The app is written in C#/C++.
  • Why would the view have both responsibilities to fetch AND to display users ? Pass the fully built users to the view and let it do its only job which is displaying data. – Steve Chamaillard Jun 8 '18 at 11:54
  • The view doesn't need to have this responsibility - if there is design which allows to avoid it. (That's what this question is partially about, too.) However, in my design (which might be wrong), I have instance of UserEventView and UserEvent and (currently) nothing more. I don't know (and don't care) whether this particular UserEventView wants to display user names or not (and just count, for example). So non-gui code preparing the data a) would need to know whether it's going to be passed to gui which needs the user names or not, or b) always fetch user names even if they're not used. – Tom Jun 8 '18 at 13:45
  • What you're saying shows it doesn't matter what the view uses the users for. It simply uses them. So inject the users into the view with dependency injection for example. And only the top level of your design will be able to fetch the users and will then pass them to the view. In a MVC design, that'd be the controller. Keep in mind that your view is about formatting and displaying data. It shouldn't care where the data it receives come from. – Steve Chamaillard Jun 8 '18 at 15:05
  • Exactly! "Keep in mind that your view is about formatting and displaying data. It shouldn't care where the data it receives come from. " - that's what I'm trying to achieve. But "formatting" might mean to fetch additional data. Another example - I have some enumerator and a view to display a selected value. There is a list of user readable names (colors, icons, ...) and the view might need to fetch this list. And even thought the list is part of gui (and not data), it cannot be part of the view - it's shared across the gui and kept in separated config and fetching this is similar problem. – Tom Jun 9 '18 at 8:42
  • Anyway, do I understand correctly that you're suggesting as a solution to my problem dependency injection? – Tom Jun 9 '18 at 8:42
1

Create a repository to wrap whatever stores you users with appropriate methods

IUserRepository
{
    GetUserById(string id);
}

Instanciate a concrete instance of this at the root of your app

class System
{
    public void Main(string[] args)
    {
        var repo = new MyUserRepo();
        ....
    }
}

and pass it into object that need it via construction parameters. ie.

class UserViewModel
{
    public UserViewModel(IUserRepository repo)
}

Now your various ViewModels and/or Controllers etc can get Users without being tightly coupled to the rest of the App.

Note: Although this pattern is usually used to wrap a database, theres no reason why you cant wrap a List or Dictionary of in memory Users instead

  • Do I understand it correctly that this is more-or-less same solution as my 2nd (but instead of passing Users, IUserRepository is being passed)? Meaning that if UserViewModel is deep in hierarchy, the IUserRepository instance must be passed through all parents? – Tom Jun 8 '18 at 11:28
  • yup, although if you have parents outside of your general framework its a bit of a code smell – Ewan Jun 8 '18 at 12:40
  • I'm not sure how to understand that last comment, but it's not problem to modify the code to pass these things around, but some gui screens are huge and so their constructor would need like 20 XYZRepositorys for different part of gui and that's what I'm trying avoid. (Also, I cannot pass it through constructor, because the repository can change during the application execution.) – Tom Jun 8 '18 at 13:47
  • sounds like you have more problems than just the example outlined in your question. Injection is great but its not magic – Ewan Jun 8 '18 at 14:00
  • I'm not sure what do you mean. (The gui is simply complex and my point was that different elements need lots of different repositories in total - and constructor of main screen would need to take a lots of arguments.) Anyway, do I understand correctly, that what are you suggesting is generally dependency injection? (Not only through constructor, but - for example - during deserialization phase (loading gui)?) – Tom Jun 9 '18 at 8:48

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.