6

I have an architecture problem/doubt and I'd like some insight on this.

Context :

We're in a mobile app that solely relies on webservice calls to be used. There can only be one user logged in at any given time and all calls require a user. We're talking about (for the sake of examples and clarity) meetings (one user has a meeting) and visitors (a visitor is a contact, not another user).

The debate :

We've been arguing for about an hour. One side wants to have several "Managers" or "Services" that handle, say, the meetings and take a user in parameter. This service/manager is called when needed to get a list of meetings, and is given the current user. This looks clean, not coupled (well separated), and pretty clear without being cluttered and complicated.

The other side say that there are no actions that a user can make without being logged in, and all those services will need a user as parameter. So we might as well have all those services called by the user itself, and simply use user.GetMeetings(); when we want those meetings. This user would be able to check all that by itself, .isLoggedIn, or .logIn, and much more.

One one hand, this looks "simpler" on the outside, we always call a user method and our viewmodels don't know about anything but a user. On the other hand this really just looks like a giant wrapper that pretty much covers the whole application (which extends further than meetigns and visitors).

There would be that user layer that does everything through the current user, and all those services would still exist but only the user would call it, and we're just turning in circle because " the user is everywhere anyway, and the services would be called everywhere anyway ". But the user doing everything looks so tightly coupled to the services that it feels wrong, even though only him uses it and ever will.

Anyway, I'd like to know what you guys think, is there something better? Is there a standard? Is there a wrong answer in my two situations? Note (and that's important) that we're only debating in the context of our app, not in general. Obviously if users had to interact with each other, if you had anonymous users or many more situations, this wouldn't work. But we know that this will always stay the same, the user will always be needed for the webservice calls, and there will always be only one user. I've been sitting there and listening to them and I'm just confused, and curious to have an external feedback on this.

I'm not talking about a massive big god-class for the User. In both cases all the calls are made in separate services/manager classes. The big difference are the following :

  • Case 1 : My model knows the current user and the service it needs (eg: MeetingService ), and simply asks the meeting service to get all the meetings and gives it the current user as a parameter.

  • Case 2 : My model only knows the current user and asks him to get all the meetings ( .GetMyMeetings();), without caring how he does it. The user itself simply knows all the different services he might need and, in that case, being calls the MeetingService and returns whatever was asked.

We're really just adding a layer into our hierarchy, so that they all combine in the user.

But code-wise, the user won't get that big, it'll probably end up being about 100 lines long, including properties and all the methods we'll need.

Robert Harvey
  • 198,589
  • 55
  • 464
  • 673
Gil Sand
  • 2,173
  • 3
  • 18
  • 22
  • 3
    Having a god class is always a bad idea. – BЈовић Sep 01 '15 at 11:08
  • Agree with @BЈовић; putting all this functionality into the user class will just create one giant god-class. Separating the functionality out into separate services, and using the user as a parameter, is by far the better approach. – David Arno Sep 01 '15 at 12:29
  • Wait wait, it's all separated in different classes, in all cases. The first choice is to let the user class call the other service classes, or to directly call those services from the model. Pretty much choosing between having all models know about the services and the current user, and making the calls, or having the models only know about the users and asking the user to "get the meetings" without knowing how the user does it. – Gil Sand Sep 01 '15 at 12:32
  • And the user itself knows all the services. – Gil Sand Sep 01 '15 at 12:32
  • I edited to give more clarity about the matter – Gil Sand Sep 01 '15 at 12:36
  • I wonder if Conway's Law is at work here: https://en.wikipedia.org/wiki/Conway%27s_law – JeffO Sep 01 '15 at 13:49
  • INteresting, because "socially" (and this is also visible on the web version of the app), users DO manage everything. Or at least up to their rank level (admin, etc.) – Gil Sand Sep 01 '15 at 13:51

3 Answers3

5

Unless User must have a Service to do user things, do not make the user pretend to be the application context/ overall state.

"Convenience" and "LOC per class" are not design principles.

Focus your classes sharply. Fanatically adhere to single responsibility principle.

the user won't get that big

Irrelevant.

it'll probably end up being about 100 lines long

Irrelevant.

including properties and all the methods we'll need.

SRP violations in progress.

There can only be one user logged in at any given time and all calls require a user.

This is state that neither the User nor Service can possibly know by themselves, in isolation so to speak. This state is particular to the broader application context. So you need one or more other classes - ApplicationContext let's say, that knows this context and queries the participating objects for what they know (I, the user, am logged in for example) thus doing ApplicationContext things.


We're really just adding a layer into our hierarchy, so that they all combine in the user.

"combine in the user" is categorically not "adding a layer into our hierarchy". Adding a layer would be something like the above.

radarbob
  • 5,808
  • 18
  • 31
4

I would go with the Service.GetMeetings(user u) approach.

Here's my reasoning.

In my experience the only difference between these two approaches once you add DTOs and hosting projects for your services is naming.

If you keep the logic out of User, then User is equivalent to your what your UserDTO would have been

Your new Service Class seems like an extra 'Helper' function until you expose User.GetMeetings() through IUser and a hosting rest/wcf/own project in which case you end up with a bunch of extra classes anyway.

So overall, the Service.GetMeetings(user u) means less code and easier code reuse. you can stick your user and meeting classes in a portable class lib and share them with your app.

Ewan
  • 70,664
  • 5
  • 76
  • 161
3

Each service should be self-contained.

Most devs know that when something is "set in stone", it will change on you when you least expect it, forcing you to re-architect your solution.

You should always design your solutions to be correct from a technological stance even if it makes things a little more complicated in the beginning.

Separation of concerns is your greatest tool to preventing you from coding yourself into a corner.

Two years from now the requirements might change and suddenly you need to allow anonymous users to create a meeting (say you move to a freemium model). Now you have to re-architect your User class to either have a anonymous User object or you have to move all those meeting function calls out of the User object, into the proper location in the Meeting service.

Or you setup continuous meetings where no one person is the creator of the meeting, now the User object is completely decoupled from the creation of a meeting. The creator of a meeting may be a Company object or tied to a Room object.

You can always expect requirements to become more complex and convoluted, you should plan for that now.

Patrick
  • 2,922
  • 2
  • 21
  • 24
  • I think this is solid advice but you also have to look out for YAGNI. Code a clean and flexible implementation so IF the time comes you can change but in the mean time you don't need it so don't worry about it. Adding unneeded complexity can be just as painful as not having enough flexibility. In the end it is all a balancing act at the developers discretion complexity vs time vs need vs flexibility. – pllee Sep 01 '15 at 16:55
  • Your third paragraph is meaningless; there's no such thing as "correct from a technological stance." – Robert Harvey Sep 01 '15 at 17:17
  • @RobertHarvey I probably should clarify, that phrase. If you're doing OO programming, then you need to follow that paradigm, if you're doing functional programming, then follow that paradigm, etc. There are correct ways to implement design for a specific paradigm. – Patrick Sep 02 '15 at 13:28
  • Every modern programming language in widespread use has support for multiple paradigms. C# is an object-oriented language with functional constructs; so is Java, so is C and C++. Scala's motto is "Object-oriented meets functional." – Robert Harvey Sep 02 '15 at 15:20
  • @RobertHarvey right, so it depends on the project architect's choice to pick a paradigm. Whatever style they choose is fine, as long as they stick to that paradigm for the entire project and that would determine the "correct" way to do solve any problems. – Patrick Sep 02 '15 at 17:34
  • Um, no. It's perfectly fine to mix and match paradigms. C# is an object-oriented language. Linq is essentially a functional programming library. Using the two concurrently is a perfectly acceptable approach. Functional techniques are a perfectly valid technique for improving your object-oriented code, especially in the area of concurrency. In short, the dividing line you're describing doesn't exist. – Robert Harvey Sep 02 '15 at 19:11