Let’s assume the modeling of User
model in a context of a social network.
User concept is composed of two notions:
- Authentication elements like userName/Password/Email etc…
- Extra data information sometimes called “User profile” like firstName, birthday, pictures etc..
At first glance, this analysis involves separation of tasks/responsibilities if we want to keep SRP.
However, typically in the case of a social network, userName may be seen as a pure information belonging to a user’s profile rather than a pure element of authentication.
Thus, there is three ways, according to me, to model the User
concept.
First, the whole in one class:
User
(userName, password, email, firstName, birthday, picture etc…)
Second, a one-to-one relationship between User
and UserProfile
:
User
(userName, password, email)
UserProfile
(firstName, birthday, picture etc…)
Third, a one-to-one but with a redundancy of the common fields (being as focused on authentication as a visible user information on the website):
User
(userName, password, email)
UserProfile
(userName, firstName, birthday, picture etc…)
Why repetition here? Of course for consitency and at the same time to avoid joins in cases of relational database when one want to retrieve each Use’s profile data.
What is a good practice to model these both concepts?
Where should I place userName
field?
Dilemma being: keeping KISS (Keep it simple stupid!) or SRP 😉
3
IMO this is a false dichotomy. If you follow SRP, you keep your system simple overall. Multiple small classes tend to be more “simple” (in many cases) rather than fewer large classes. Plus it seems like you are conflating two issues: how you design your classes vs. how you design your database. The two do not need to be related.
In your given case, it sounds like you probably have 3 classes: User
(username, password, email), UserProfile
(name, birthday, etc.), and UserPictures
(the pictures, because why would they be part of the “profile”?). Then you can create your User
class to include (via composition) the other two.
Of course, this is just a rough attempt at design without any broader overall picture of requirements. There are plenty of different valid, simple, and SRP ways to do break down your classes based on the actual need. The main point is just that KISS and SRP aren’t inherently at odds.
Edited much later after learning more:
Back when I wrote this answer, I was thinking about SRP in terms of “a class should only have one responsibility” which has a lot to do with cohesion and coupling. This is good, but I have since learned more about what SRP really is.
The book Clean Architecture has a good description about SRP (and all of SOLID and more). Originally the definition was “a module should have one, and only one, reason to change.” But Uncle Bob revised it to be “a module should be responsible to one, and only one, actor.” So it’s more about the use-cases of why a particular persona might want things to change.
To tie this back to the OP, classes that control behaviors for things like passwords, birthday, and pictures might all be responsible to different actors. Your security team cares about passwords and may cause you to change those behaviors separately from product managers who want to edit how birthdays are handled. So then, to follow SRP it would be wise to understand who those stakeholders are and create separations (e.g. separate classes) based on those stakeholders. Thus, you might have a UserCredential
class for managing passwords instead of putting it in another class along with data that changes because of other stakeholders’ reasons.
Following SRP keeps things simple in many of the same ways I mentioned originally, but it also makes things simple when you don’t mix changes made for different reasons in the same class. So even with this revision, SRP and KISS can still live happily together.
6
You’re confusing KISS; simple isn’t referring to simple to implement for the developer, it refers to simple design, simple to understand and use. Following SRP makes your code simpler to understand and use because it’s simpler to use a purpose built class for a single purpose than a multipurpose class, it’s also simpler to maintain.
SRP supports Simplicity, even if it’s more work for the developer to implement, the result is a simpler system.
3