6

I have been working on a project that deals with, let me simplify, Schools and Students.

The general requirement is that certain Students could be assigned to certain Schools to work an internship.

But the Schools are not only Schools per se, those can be Hospitals, Prisons, Nursing Homes, etc..

Since I'm using an ORM and Entity classes, my first idea was to use Inheritance mapping, like, to have a parent class InternshipProvider with particular child classes...

However, another requirement is that this should be done so that any User in future can add the new instance of InternshipProvider by using a form AND at the same time to be able to add some specific properties. Sometimes to the class, and sometimes only to particular instance.

Per each instance, the InternshipProvider may have or not, a set of specific properties, which could be available on a form as text fields or choice options, or a subform, etc..

So I kinda solved it like this: There is the InternshipProvider class, among standard properties it has a long text property as well. And inside of that text property (field) are JSON key-value pairs, describing those specific fields and values stored.

There is also a "monster", custom form generator feature, that handles this fairly OK. Each InternshipProvider instance has a specific custom form assigned to it for future editing.

There are two reasons for these requirements:

  1. They want to eliminate a need for hiring a software developer in future.
  2. The domain IS complex and unpredictable.

Do you have a better idea? Is there some concept that covers this? Some design pattern or the likes? What would you say to the Client?

Thank you

Đuro Mandinić
  • 216
  • 1
  • 7
  • 2
    What CandiedOrange says. You seem to be obsessed with different kinds of institutions but do their roles in society really matter to you? I read school, student and internship. That makes me think periods, reports, grades, mentors, addresses, contacts, major subjects. And I do not see these entities vary as new types of institutions are added to the collection. Ask yourself (or better yet, your client): what are you modeling and to what end? – Martin Maat May 23 '17 at 22:43
  • 1
    One problem is that client may come from background where everyone was using its own excel custom format, you need to make customer understand that with a shared system, this is not possible anymore ie you can't have 2 users defined different fields for the same type of objects that make no sense. – Walfrat May 24 '17 at 08:11
  • Sorry, I did not make it clear that I'm not using polymorphism. I edited the question. – Đuro Mandinić May 24 '17 at 09:02
  • 7
    Note that many systems designed with the intent to allow complex customization without requiring a programmer succumb to the [Inner Platform Effect](http://thedailywtf.com/articles/The_Inner-Platform_Effect): The customization system itself becomes so complex that only a programmer can handle it. So it would be easier to just let a programmer do the change in the program code directly. – Philipp May 24 '17 at 10:32

4 Answers4

13

I've no idea why Hospitals, Prisons, Nursing Homes, etc need their own classes. Nothing here makes me think they do. Why aren't these simply different instances of InternshipProvider?

Not everything with a name needs to be a class. You need polymorphism if a Hospital needs the same method to have a different implementation than a Prison. Otherwise, these classes are pointless.

The only "unpredictable" thing I can see here is what the next InternshipProvider will be called. Show me some behavior that needs to be different based on which InternshipProvider and I'll rethink this.

candied_orange
  • 102,279
  • 24
  • 197
  • 315
  • I thought they need to be child classes at first, because they have some mutual properties and each of them would have a few specific properties. But I did make it as one class, the InternshipProvider. The specific properties are to be added later by ordinary Users. – Đuro Mandinić May 24 '17 at 07:59
  • 1
    CandiedOrange : basically those classes are entities that share some common properties and have a set of specific property. While your answer is good I think it lack the proposal of a solution, or a list of available solution to not use inheritance : Entity Attribute Value like John Wu considered ? Composition over inheritance ? Something else ? – Walfrat May 24 '17 at 08:10
  • Some more info: The InternshipProvider has a property named Classification. A Classification can be named as School, Hospital, etc.. Quoting the Client: "A School should have specific fields, e.g. DEIS Y/N, Gender (Male, Female, CoEd), etc. while other Provider types (e.g. hospital) will not need these particular fields but may need others. User must be able to add additional fields." – Đuro Mandinić May 24 '17 at 08:25
  • So you use interfaces? IInternShipProvider interface which can be implemented by hospitals but also by schools etc. ? – Pieter B May 24 '17 at 11:08
  • @Pieter B: I don't see behaviours there. Do you see any? I'm serious ;) – Đuro Mandinić May 24 '17 at 11:21
  • "I thought they need to be child classes at first, because they have some mutual properties and each of them would have a few specific properties." This is when you should start thinking about an interface. Have different classes with some common among them behaviour. – Pieter B May 24 '17 at 11:40
  • @ĐuroMandinić please note edit. – candied_orange May 24 '17 at 12:53
  • @CandiedOrange Actually, I'm using PHP and Symfony2 with Doctrine. How would you map the Collection for Hibernate? – Đuro Mandinić May 24 '17 at 13:09
  • @CandiedOrange: your answer was fine until you added this monster of decision diagram which is IMHO too far away from the OPs question. – Doc Brown May 24 '17 at 13:25
  • @DocBrown sigh fine – candied_orange May 24 '17 at 13:30
  • Due to my initial mistake in question text to make clear that I am not using polymorphism, this answer got the most votes. But it's obviously missing the point. – Đuro Mandinić May 25 '17 at 10:48
5

The pattern you are looking for is called EAV or Entity Attribute Value.

In this pattern you would set up a table with a row for each attribute and another table with values for each entity x attribute combination.

Values are stored as strings, but you can define meta data in the attribute table to tell the system how to parse the string, e.g. as a date or integer. If you don't have metadata, it makes validation difficult and sorting will come out wrong.

EAV isn't always the best in terms of performance, but it will perform much better than a giant JSON string, especially if you need to sort or filter based on these attributes.

John Wu
  • 26,032
  • 10
  • 63
  • 84
3

I would go for a Dictionary of 'Custom Fields'

public class InternshipProvider 
{
    public string Id {get;set;}
    public Dictionary<string,string> CustomFields {get;set;}
}

mapped to the following table structure

TABLE InternshipProvider
    varchar Id ('myguid')

TABLE CustomFields 
    varchar Id
    varchar ParentId ('myguid')
    varchar ParentType ('InternshipProvider')
    varchar Value
    varchar TypeToCastTo?

You can expand this to other types that require custom fields.

Then for querying you can do something along the lines of...

InternshipProviderList.Where(i=>i.CustomFields[keytosearch] == valuetosearchfor)

or some dynamic sql fun

Now obviously the more complex you make your acceptable query logic, the more complicated it becomes to write the query.

You can see how you might insert different functions for different comparators and then maybe loop over multiple clauses, maybe work out how to handle AND vs OR, brackets, dealing with nulls etc. At some point (quite soon) you require programmers to write the query.

I have found that its usually easier just to hire a programmer to add the new logic you require as it comes up than try to write a generic system that can handle everything AND do it in a way that is simple enough that your users don't have to be trained in the meta-programming language of the system.

Not sure I would tell the client that though, maybe mention the risk, but give them what they want.

Ewan
  • 70,664
  • 5
  • 76
  • 161
  • Might make sense to make `CustomFields` be `private set` and initialized to an empty dictionary. The dictionary is mutable either way; there's not necessarily any purpose in having the value of `CustomFields` be mutable *and* reassignable. – Tanner Swett May 23 '17 at 20:54
  • brevity of example code is the main reason – Ewan May 23 '17 at 21:27
2

There is no reason for convoluted solutions like saving JSON. You have a table where each row is an internship providers. Then you have a table with internship provider properties, where each row describes a property associated with an internship provider through foreign key.

And if a property in turn have options which are associated with a specific internship provider, you just further create a table with options, with a foreign key to the internship provider property.

It is easy to create a form which adds a row to a table.

JacquesB
  • 57,310
  • 21
  • 127
  • 176