The Design of Software (CLOSED)

A public forum for discussing the design of software, from the user interface to the code architecture. Now closed.

The "Design of Software" discussion group has been merged with the main Joel on Software discussion group.

The archives will remain online indefinitely.

Handling Per-Customer Customisation

I'm creating a .NET command line app that takes a customer, shipment number and filename and outputs an XML file containing the relevant information required by the user.

Typically most customers will be happy with an industry-standard XSD.

However, some customers want different things:-

Customer A might want dates in a different format and their ISO Country Code appended to the shipment number present within an element of the resuling XML file.

Customer B might want three optional (in the XSD) elements supplied, containing data from a custom-lookup table (maybe their own product codes for the goods shipped)

So the raw data read from our database may come from different tables for different customers, or may require formatting differently and/or the output file format whilst validating against a published XSD may still vary tremendously from customer to customer. I'd guess the XSD has around 300 elements (nested to about 6 levels) with about 50% of the elements being optional in the XSD. Therefore, there are many combinations of output for the resulting XML files.

I was going to create a "general" ShipmentMessage class that basically created a "stock" message, populating and outputting all the elements (even the optional ones) breaking the message creation up into lots of overridable methods within the class (a lot of methods!) and then for each bespoke customer, a class that inherits from the ShipmentMessage class and overrides the appropriate functionality.

A really, really, simple example might be:-

ShipmentMessage

build() {
addHeader()
addAddress()
addFooter()
}

and in the bespoke customer class

CustomerAShipmentMessage

I could either override addFooter() say to modify the behaviour of the footer or override build() if perhaps I wanted to exclude the Address for example
build() {
addHeader()
//addAddress()
addFooter()
}

Obviously this is a gross over-simplification of what I have to write.

Does this method sound reasonable?

Will it provide the best method for making the least changes for bespoke customer messages (for me)?

If you recognise a GoF Design Pattern that seems appropriate then please let me know.

Any other suggestions very much welcomed!
Squidward Tentacles
Wednesday, January 24, 2007
 
 
Much simpler way...  implement a XSL Transform after XML creation but before delivery (Adapter?  Two-Step Something?).  By default, no transform would be applied.  If a customer wants something custom, you create the corresponding XSLT and associate it with their customer account.
KC Send private email
Wednesday, January 24, 2007
 
 
I don't know that using an XSL transform would work for the example of needing to bury customer-specific product codes inside the data. Unless the untransformed version contained the product codes used by EVERY customer.

XSL transforms seem to me like one of those things that work OK in simple situations, but collapse under the weight of anything moderately complex.
Greg Send private email
Wednesday, January 24, 2007
 
 
From the today's daily WTF (codesod)

http://thedailywtf.com/Articles/Configuration_Made_Easy.aspx

I hope that won't help you :)
Masiosare Send private email
Wednesday, January 24, 2007
 
 
Jason Send private email
Wednesday, January 24, 2007
 
 
Squidward, are you getting enough money per license sold to make customization worthwhile?

As Joel has often said, this is a trap. It moves you from the software business to the "consultingware" business. At some point in the future, you will spend more time and effort placating each customer than you will be doing actual development. In this specific case, it sounds like they're trying to get report designing services out of you all for the price of the original software.

Assuming you've decided to go down this route, you have a couple of options...

1) Support a third party program such as Crystal Reports, allowing them to format the reports to their liking.

2) Provide an API to access the data.

3) Literally create a form designer.

4) Prepare an extensive configuration file such that the build class can parse it and decide if it needs to include the address block or skip it.

5) Bill customers on a per report basis.

6) Tell the customers to suck it up and accept the templates you've provided.

None of these solutions require you to recompile your application each time a customer asks for a new report. By drawing a line in the sand there, you're saving yourself big time on testing and debugging costs.
TheDavid
Wednesday, January 24, 2007
 
 
Thanks to you all for taking the time to comment.

Hmmm, hardcoding the changes a la The Daily WTF. A big If...Then...Else block, now there's a thought! <g>

I work for a manufacturing company as an internal IT developer and the "customers" I'm bespoking the messages for buy our product (non-computer/software related). The messaging is really a value-added service which is becoming more of a requirement than it used to be. As our competitors also provide such services, we need to meet our customer's requirements to stay competitive.

KC, I'd originally thought about XSL transforms but as Greg correctly points out, we're talking about the data source for some customers being completely different so the behaviour is customised way before the XML is even generated. I know XSL transforms can do some clever stuff, but I'm not sure they are the right tool for this task. Even if they had the functionality to do it all, the resulting XSL transform files would be a nightmare to maintain/modify for new customers, probably more so than a nice, friendly C#.NET class.

I've thought of creating a templating framework such that customer's messages can be bespoked by changing either the template itself OR the value of the tokens replaced within the template. Therefore customer A might have a template:-
<Shipment><ID>%%shipmentid%%</ID></Shipment>

and customer B might have:-
<Shipment optionaldate="%%shipdate%%"><ID>%%shipmentid%%</ID></Shipment>

Customer C could use customer B's template but change the format of %%shipdate%% and/or %%shipmentid%% to further customise the message.

While this looks tempting, we're dealing with up to 6 levels of nested XML where often logic dictates whether elements are displayed or not in the resulting XML and so the template engine would need some thinking about - it probably wouldn't be a simple "token replacement" exercise.

If the sub-classed shipment message per customer idea is feasible, then it would be easy to keep customer/messageclass mapping in a database and instantiate the appropriate class at runtime to produce a message. It certainly sounds the most flexible option but I'm concerned by how much I'll have to break down the message building process to provide enough granularity to allow sub-classes to override just the behaviour they want without having to re-write the whole class each time a new customer wants something slightly different.

However, I'm certainly still open to any other suggestions!
Squidward Tentacles
Wednesday, January 24, 2007
 
 
"...and instantiate the appropriate class at runtime to produce a message."

At the very least, I would discourage instantiating new classes simply because it requires you to add, test and debug a sub-class every time you added a new customer.

But you could continue with the template suggestion and do something like this...

<html>
  <head>#HEAD#</head>
  <body>#BODY#</body>
  <foot>#FOOT#</foot>
</html>

...and substitue in the appropriate strings for #HEAD# and so on at runtime.
TheDavid
Wednesday, January 24, 2007
 
 
How about creating a variety of solutions and picking one for each task based on which fits best.  I support an app that has a good deal of customization and we use combinations of the following:

Config switches for features that are likely to serve many customers or that may change in the field.  These require a lot of unit testing, but cover a lot of bases.

Template based solutions like XSLT or even custom ascx files for a client.  These are actually fairly easy to test and fairly stable if they are close to the output stage of an application.

Custom assemblies loaded through reflection.  We use a plug-in concept where every assembly in a certain directory that exposes any of a given set of interfaces are loaded.  Each interface has a specific hook point in the application to be called.  Sometimes they are sub classes like described in the original post or sometimes they are a post-process or pre-precess step.  We use this model for major deviations from standard design.  Hell to test, but better than parallel app builds.  A well designed interface is the key to keeping sanity.

Custom scripting.  Kind of the same idea as custom assemblies, but compiled from source at runtime.  More flexible than custom assemblies and generally designed for the customer to implement.  Interface design is important here too.

I don't see why you would have to choose one for all of your customizations.  I also would stay away from all customers always having a child class.  That's too much like supporting parallel builds, but with extra unneccesary complexity.

BTW, if you are using a Microsft XML parser, it supports VBScript in XSLT.  That might allow you to get away with just XSLT for a lot of your solutions.  I especially like the fact that the parser will choke on the smallest well-formedness error and the fact that is nearly impossible to attach a debugger to it.
JSmith Send private email
Wednesday, January 24, 2007
 
 
How about porting the app to something easily scriptable, such as Python?

Then provide an api for the customers, along with some docs & videos to show them how to customize their own output.

For the customers that want more than this, make it an additional service that the company will provide for a fee.
*myName
Thursday, January 25, 2007
 
 
JSmith -- Thanks for all the suggestions. Using "best fit" technology for each step in the message generation chain obviously makes sense.

*myName -- The thought of creating docs and videos to explain how customers can customise the output when I'm still not sure myself is somewhat daunting. Recharging the customer directly is not an option. Adding a bit to the selling price of the product is feasible but pricing happens at a much higher level than I and is dependent on a lot of factors that make a month or four of my development time fairly insignificant!

TheDavid -- I'm being drawn to the template solution however it still doesn't answer the crux of the problem which is WHERE does the customisation of the replacable tokens take place? I still need Customer A's #HEAD# to be "<ShipId>1234</ShipId>" and Customer B's #HEAD# to be "<ShipId foo="bar">GB_5678</ShipId>" (really, really, over simplifying things here!).

Are we back to having per customer classes that populate these tokens as appropriate for their customisation, a giant switch statement (heaven forbid) or even the Daily WTF's If...Then...Else "joke" metioned by Masiosare and Jason earlier in this thread!

Primarily I'm a classic ASP developer but have done a bit of stuff in .NET. I did develop some proof of concept plug-in code much like yours that loaded custom assemblies through Reflection based on a known, exposed Interface. This certainly looks like it might be the way to go?

If I went with the template option, where would you suggest I place the token customisation code?

o Some kind of Factory pattern instantiating per-customer classes?

o Plug-in assemblies, perhaps even passing the messagehandler type on the command line of the application so I could easily produce a gamut of messages for a single customer just by changing the assembly name passed on the command line?

o Something else?

Thanks again for everyone's comments. It really helps to have the input of a bunch of folk who seem to know what they're doing or have done something like this before.
Squidward Tentacles
Thursday, January 25, 2007
 
 
The current wisdom seems to not do customizations, but rather use configurations that users can manage themselves. Look at the following resources:

Multi-Tenancy, metadata driven everything and you are my #1 customer http://blogs.msdn.com/gianpaolo/archive/2006/02/26/539717.aspx

Architecture Strategies for Catching the Long Tail
http://msdn2.microsoft.com/en-us/library/aa479069.aspx#docume_topic9
Stacy Murray Send private email
Thursday, January 25, 2007
 
 
Stacy Murray -- Thanks for the links, they both looked interesting with the "Long Tail" article having some good detail on different multi-tenant database models of which the Shared Database, Custom Extensions model is one I've used before, although I wasn't aware of this "tenant" and "multi tenant" terminology!

In this article is a link to more specifics on the various data models at http://msdn2.microsoft.com/en-us/library/aa479086.aspx which I will certainly be reading.

This article mentions that "Later in this series, we'll look at ways you can help tenants put their data model extensions to good use through presentation and workflow customization." which is exactly the article I think I need for my message output formatting "customisations" but either it's not been written yet or I simply can't find it.

Certainly this looks like a step in the right direction, thanks for the heads-up!
Squidward Tentacles
Friday, January 26, 2007
 
 

This topic is archived. No further replies will be accepted.

Other recent topics Other recent topics
 
Powered by FogBugz