8

We're developing an app that has slightly different requirements for each business market (countries and states) that it's available from. It seems like a common situation but I can't seem to find a good article about structuring code/modules for this scenario.
It's a C# app and we're debating between Strategy vs Template patterns but there's also the consideration of folder structure and naming conventions. It seems like a separate project for each state would get unmanageable quickly (e.g. 5 core services X 50 state-custom projects) = 250 projects!!) Perhaps 1 custom project per service handling specializations organized in sub-folders per state?

  • Can you add more specifics about what exactly is different between states? – paj28 Oct 29 '16 at 12:45
8

Have a single app and design the appropriate configuration solution.

JacquesB hints at this, but I want to state it more strongly. You must do this by configuration rather than developing 50+ versions of the codebase. Anything else will be insanely unmanageable.

If there are complex requirements which can't be handled by simple configuration parameters, design a more complex configuration solution.

Your configuration might even have a simple scripting language that defines logic requirements. That is ok. The key is that the "configuration" must be cleanly separated from the codebase of the app.

Otherwise, things like identifying and fixing bugs will be a mess. With normal versioning in addition to locale-specific versions, you will have hundreds of versions in the field. And you won't be able to easily tell whether a bug is related to the locale-specific code or the base code.

Start thinking about edge cases now.

This is a situation where there is a high risk of making bad assumptions that will be incredibly costly. Such as "Each app user will only ever need to operate within one locale." Is that true? Make sure you think really hard about such issues as early as possible.

  • We're setting up a configuration solution using AutoFac Multitenancy. Any advice on organizing and naming the components (classes/projects)? – Kim Oct 28 '16 at 14:42
  • Yes. I was going to comment that this is a great example for Dependency injection. You can define the common interface and create specific components that are then passed in per some configuration. this should allow for much better testability and maintainence than custom scripting as mentioned in another answer. – Fran Oct 28 '16 at 14:45
6

It really depends on what is different. Is it something which can be expressed purely by configuration (eg. tax rates), or do you really need separate code logic for each state? The more you can handle purely by configuration the better!

You most likely does not need separate code by state. For example you mention that some markets issue refunds while other issue replacements. This is still only two code paths you need to test. Then you can have a configuration indicate which path should be used for which state. This is much better that having separate code for each state. You still only need to test two code paths rater than 50.

If you ever feel the need to write individual code for each of 50 states, you are probably doing it wrong. You should definitely not have separate projects per state, or even having "50 different" of anything except configurations sections.

  • it's more than just taxation but the extent of differences is unknown at the moment. We're also dealing with multiple countries. There's workflows can also be slightly different such as some markets refund purchases while others issue replacements. And certainly different pricing per market. – Kim Oct 28 '16 at 14:35
  • What JacquesB is getting at is abstractions. IOW, how might things that look different actually be the same? "All states charge tax" Now I have a general concept I can encode universally: "CalcTax()". And then all you need is the specific tax rates (tax table) - that is configuration. Hear me now and believe me later, "configuration data" - the abstract concept! - will greatly simplify code. – radarbob Oct 29 '16 at 17:06
  • If there's a lot of overlap in functionality between the states (beyond an effective "default" option), this is sound advice. If states are prone to nuance that makes it difficult/dangerous to lump them together as a kind of "Option B", it isn't. The OP will need to figure that out. There's also a danger of headache should one state pass new laws forcing code updates, but that's going to be true of any conditions where multiple states are using the same code. – BlairHippo Oct 31 '16 at 17:05
5

I've had some practical experience with such projects, and can at least offer some general guidelines.

  • JacquesB is absolutely right that configuration is your bestest friend. Whether you put that configuration in files or in database tables is a stylistic choice (though I'd be leaning towards config files, since once you get a service set up, I'm willing to bet it won't change much).

  • Expect code that looks a lot more indirect than what you're probably accustomed to. There will be many places where instead of saying "Do this next," you'll be saying "Ask the config for what we're supposed to do next, then go do that". Which can be a development headache, but you'll get used to it.

  • I'd recommend a structure of "These are the common elements that everybody probably wants, and these are the bespoke elements hand-designed for individual states." To that end, correctly identifying that default functionality will be a big part of how easy/awful the code is to work with in the future. Be prepared to do some refactoring when you learn that more stuff than you thought needs to be configurable.

  • Always make sure that any error messages coming from the custom elements identify exactly where they came from. The huge number of different execution paths mean that debugging will suck no matter what; anything you can do to reduce the amount of easter-egg hunting will be more than worthwhile.

  • Invest the time to make a good automated test suite. A REALLY good automated test suite. This is always good practice, but for you, it could literally mean the difference between success and failure. Again, it's the huge variety of execution paths; any modifications to the common code will leave you vulnerable to butterfly-effect bugs in branches you weren't expecting. You need to catch those bugs before they hit production.

  • Thanks. Any suggestions for naming classes, grouping projects? Should the implementations be suffixed with the market name or try to describe the different functionality? – Kim Oct 28 '16 at 14:50
  • I'm not a c# guy, so I can't speak to specifics of how you name classes. But honestly, I'd think you'd want a package name that incorporates both market AND functionality, so you can tell at a glance what it's supposed to do. Something like ourproject.calculations.tax for the default and ourproject.florida.calculations.tax for whatever custom mods Florida law forced upon you (if any). Where you put that market name is a point of discussion, but I'd be inclined to make it easy to see what customizations a particular market is forcing upon you. – BlairHippo Oct 31 '16 at 17:02
2

Although config can help its not going to solve all your problems an it may create new ones.

By far the best way in my experience is to create a multi tenancy solution which can handle all or any of the states(tenants)

This will require configuration. ie sales tax percentage by state but also conditional code LegalMethodOfCalculatingWorkingHoursA vs B

If your app is hosted/online you could take microservices approach to the conditional code but an off line solution will have to bundle all the optional modules and select between them

You will find some modules reused across all states, such as sales tax and some crazy law ones only used in a single state. You will also find that laws change over time. so the modules used will vary by time within a single state

So I would go with naming by functionality rather than state.

  • I like this - code branches based on functionality, e.g. "must ask address" "delivery is tax free". Then configuration to that maps each state to a set of functionality. – paj28 Oct 29 '16 at 12:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.