4

I've built an open source application, and I'd be curious to know how others are handling customer-specific requests. It's important to me to keep the app simple; I'm not trying to make it all things for all people. Apps can get bloated, complex, and just about unusable that way. However, there are some customer-specific options that would be nice (it just wouldn't apply to all customers). For example...

Say we have a domain entity called Server. In the UI, we let a customer pick from a list of servers. For one company, it's helpful to filter the servers by location (US, Germany, France, etc...). It would be easy enough to add a server property like this:

public class Server
{
    public Location Location { get; set; }
    // other properties here
}

My concern is that Server could become bloated with properties over time. And even if I only add location, not all customers would care about that property.

One option is to allow for user-defined fields:

public class Server
{
    public string UserField1 { get; set; }
    public string UserField2 { get; set; }
    public string UserField3 { get; set; }
    // etc...
    // other properties here
}

Is that the best way to handle this? I don't like the fact that type safety is gone by making everything a string. Are there other/better ways that people are handling issues like this? Is there even a design pattern for something like this?

Bob Horn
  • 33,387
  • 34
  • 113
  • 219

6 Answers6

2

In my opinion, a good design pattern for something like this is to use schemas at the database level and then basic inheritance at the class level.

CREATE TABLE dbo.A (
    ColumnA INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
    ColumnB VARCHAR(50),
    ColumnC INT,
    etc.
)

And now we have a client who needs some specific functionality, so let's create an extension to this table in a different schema:

CREATE TABLE CustomerA.A (
    ColumnA INT NOT NULL PRIMARY KEY,
    Location VARCHAR(50)
)

But now we have another client who needs to extend it differently:

CREATE TABLE CustomerB.B (
    ColumnA INT NOT NULL PRIMARY KEY,
    DataCenterID INT
)

Though the fields may not be relevant, you get the idea, and so now we need to build the customer specific domain models here:

public abstract class A
{
    public int ColumnA { get; set; }
    public string ColumnB { get; set; }
    public int ColumnC { get; set; }
}

public class CustomerA_A : A
{
    public string Location { get; set; }
}

public class CustomerB_A : A
{
    public int DataCenterID { get; set; }
}

And so now when we need to build something for Customer A, we'll build their subclass, and for Customer B theirs, and so on.

Now, FYI, this is the beginnings of a very dynamic system. I say that because the piece that's missing, that's not yet dynamic, is the user-interface. There is a significant number of ways that can be accomplished, but way outside the scope of this question. That is something you'll have to consider. I say that because the way you manage the interface will determine how you even know to build which subclass.

I hope this has helped.

Mike Perrenoud
  • 66,820
  • 29
  • 157
  • 232
  • +1 Good, solid, OOP approach. One issue is that Location isn't just for CustomerA; it would be for any customer that wants to use location. But I get your point, in general. Perhaps the end user can define himself as an aggregate of one or more customers. The UI could handle this be enabling/disabling the functionality based on whether the customer has selected that functionality. – Bob Horn Dec 05 '12 at 21:14
  • @BobHorn, that's a good train of thought. You can break those schemas down at whatever level you feel necessary. Based on your comments, customer may in fact be a bit granular. – Mike Perrenoud Dec 06 '12 at 13:03
1

The usual approach early on is to use the config XML files for this sort of thing. But programming for client-specific needs requires a whole mindset around how you program. Refer to this answer to a similar question.

Community
  • 1
  • 1
StriplingWarrior
  • 151,543
  • 27
  • 246
  • 315
  • That link to your other answer is nice. I especially like the part about dependency injection. When a certain client uses the app, its dependencies can be injected into the container. I think I'm feeling more comfortable with handling this in business logic, but the tricky part may be the UI. Although I guess that could/should be handled the same way. – Bob Horn Dec 05 '12 at 21:27
1

Of course it always depends on how much customization you want to allow. In our product we went as far as enabling users to completely defined their own entities with properties and relations among them. Basically, every EntityObject, as we call our entities, in the end consists of a value collection and a reference to a meta-model describing the values within them. We designed our own query language that allows us to query the database and use expressions that are translate-able to any target language (although we currently only do SQL and .net).

The game does not end there and you quickly find that things like validation rules, permissions, default values and so on become a must have. Of course all of this then requires UI support, at least for the execution of the meta-model.

So it really depends on the amount of adjustment a end-user should be able to perform. I'd guess that in most cases simple user fields, as you described, will be sufficient. In that case I would provide a single field and store JSON text within that. In the UI you can then provide at least a semi-decent UI allowing structure and extensibility.

Philipp Aumayr
  • 1,400
  • 11
  • 14
1

Option 1: Say "no". :-)

And while I say that (half) jokingly, there is some truth to it. Too often, developers open themselves up to endless customization by allowing one or two custom features, setting the snowball in motion.

Of course, this has to be balanced, and it sounds like you may be doing this to an extent. But if you truly want to keep your app simple, then keep it simple and avoid adding customizations like this.

Option 2: Inheritance.

If you really need to add the customization, I would lean the way of building a base class with all "standard" options, and then building customer-specific classes containing customer-specific optimizations.

For example:

public class Server
{
    // all standard properties here
}

Then for Joe's Pizza, you can have:

public class JoesPizzaServer : Server
{
    public Location Location { get; set; }
}

The side-benefit to this is that it will allow you to base your presentation views off of the client-specific (or base) models.

For example, in MVC you could set up your view models like this, and then you could have specific views for each customer.

For example, Bob's Burgers would have its own view on the base model:

@model MyApp.Server
@* implement the base form *@

And Joe's Pizza's view would use the custom model:

@model MyApp.JoesPizza
@* implement the base form -- a partial view -- with addtional custom fields

MVC does a really good job of supporting this type of pattern. If you're not using MVC (maybe WPF or Web Forms), there are still ways to leverage partial "view" files for accomplishing something similar.

Of course, your database can (and probably should) support a similar inheritance model. Entity Framework even supports various inheritance models like this.

Jerad Rose
  • 15,235
  • 18
  • 82
  • 153
  • I wish I could give you two upvotes for your option #1. I'm a firm believer in saying no. So maybe customers don't get everything they want, but that can be a great thing even if they don't know it. Point 2 is interesting. That may be a good foundation to start playing around with... – Bob Horn Dec 05 '12 at 21:20
1

I may be wrong here, but it looks like you want to handle different versions of your software with the same code base. I can think of two approaches for this:

  1. Actually define different versions for it and handle changes for each client. This won't give you problems from the domain-modeling point of view, but will require a supporting infrastructure, which will have to scale according to your client requirements. There are some related questions out there (e.g. this, this and this).
  2. Handle this at the domain-model level, as a user-defined configuration. The advantage of this approach is that you don't have to incorporate multiple versions of your software, but this comes at the expense of making your model more generic and potentially more complex. Also your tests will surely have to be adapted to handle different scenarios. If you are going in that direction I would model an object representing the attribute (with a name and a value) and consider the Server class as having a collection of attributes. In that way your model still captures your requirements in an OO style.

HTH

Community
  • 1
  • 1
Andrés Fortier
  • 1,705
  • 11
  • 12
  • +1 for the advice. I don't think I want to get into multiple versions of the app based on client requirements. That seems like quite a bit of overhead. For point #2, are you saying to have each attribute be its own class? – Bob Horn Dec 05 '12 at 21:18
  • I would start by having an `Attribute` class with a `name` and `value` instance variables. This allows you capture your requirements in the domain model. If you need, you can later specialize this in new subclasses (e.g. read-only attributes, collection attributes, etc). You may also want to google for EAV models (e.g. see [this thread](http://stackoverflow.com/questions/4066463/should-i-use-eav-model)) – Andrés Fortier Dec 06 '12 at 11:20
0

I approach from Python that I think would work rather well hear is a dictionary. The key is your field name, the value is the, errrrr... value ;)

It'd be simple enough to represent in a database too.

Jeff B
  • 8,572
  • 17
  • 61
  • 140