The problem
I have an application that has a User
object and a Student
object. Some users are students. All students are users. In the database (django-ORM based), this is represented as a Student
table with a foreign-key to the User
table.
I'm trying to create a REST API, and an object hierarchy in the iOS app that models this API. I'm having trouble deciding how to model this.
The Current Solution
The best I've come up with is this: Have a User
model in iOS, have a Student
model in iOS which inherits from User
and extends it with more properties. Then, have a method which receives a JSON response from the server, and creates either a User
or a Student
model, depending on the dictinoary.
Finally, the server will need to always give me the most specific type. I.e., when I log in to the server, it will decide whether I'm a student or just a regular user, and will return me the proper dictionary.
Is this the best way?
This sounds a little complicated. But any other way I've thought of modeling it, e.g. changing the way the database is laid out, gives me a design in which the database isn't aware of all the constraints. For example, Student
objects are allowed to own other objects (e..g, homework_paper
). I can model this with a foreign key to a User
object instead of to the Student
object, and say that the Student
is simply an extension of the user. But then the database doesn't force the fact that a homework_paper
has to be owned by a student.
Is there a better way to solve this problem that I'm missing?
There is nothing that says the classes in your UI layer need to match 1-on-1 with your domain classes. Just like your domain classes do not have to follow your database tables exactly.
You can think of the classes for your UI layer your UI representations as a different set of classes. Classes you needed to make your UI work, or needed to use a framework to your advantage without compromising the business rules in your domain model and/or database design.
Your User UI class could well be a class that wraps both user and student domain classes. It would have knowledge of both User and Student domain classes. It is then up to this wrapper class to instantiate either a User or Student.
Another approach would be to model the User-Student relation as a "has a" relation instead of a "is a". After all, what are you going to do when user's can not only be students but teachers as well. For example when a teacher enrolls in some other course than the one (s)he teaches. Usually, these kinds of relations are better modelled using Roles than as "is a" relations. See Martin Fowler for more info on Dealing with Roles.
In any case, the User UI class would be the basis for your representation in your REST implementation and fill extra parts in that representation dependent on whether it is dealing with a user or a student or - in the role based approach - a user that has student stuff associated with it:
{
"user": "/users/1234",
"name": "Some non student's name",
"stats": {
...
}
},
{
"user": "/users/4567",
"name": "Some Student's name",
"stats": {
...
}
"papers": [
{ "paperid": "/users/papers/111"
...
},
{ "paperid": "/users/papers/222"
...
},
{ "paperid": "/users/papers/333"
...
}
]
}
Edit in response to comments
The server has to decide that anyway at some level, unless you want to have your UI differentiate between users and students at the URI level. Which I recommend AGAINST. It makes for unusable UI's. The user isn't interested in the implementation details and doesn't want to be confronted with them.
But no, if's are not needed.
Use polymorfism to your advantage.
The server can put polymorfism to good use receiving a student instance from its data access framework for any user that it retrieves by id. Students after all can always be referenced as users. So the server can simply ignore the fact that the user reference it gets from the data access framework might be a student. When the server actually needs to do/add specific student stuff, it should do so in a derived class.
No if statement needs to be used anywhere for this. Not even in the UI classes. The UI just needs to know that the User reference it receives could also be a Student and act accordingly. Preferably not by if User is Student
(that would be tying the UI way to deeply to the class hierarchy), but by if IUser implements IStudent
: asking the User reference whether it also implements the Student interface, and then getting and using that IStudent interface reference.