We're developing a "middle-tier" to replace an existing business logic/data access layer. One of the design concerns we're under is that we need to design it in a way that allows for multiple customers' databases and/or middle-tier pieces to live on the same server as a part of our hosted offering. The database schema and setup for the hosted environment is fairly set in stone at this point, as it's already in production. Essentially, on a given DB server in the hosted environment, each customer has a SQL Server instance that is named using their unique customer ID.
What we're trying to decide is whether to have a separate path all the way from the client app through the web service, business logic, and data access to the database for each customer or to have a single, shared instance of each piece, wherein the data access layer is responsible for getting the data from the correct SQL Server instance, or somewhere in-between those two. With a single shared path for everything, if any one piece goes down all of the clients accessing it are dead in the water. On the other hand, with individual paths for each customer, there's (seemingly) more to maintain, besides perhaps being overcomplicated? Here's a horrible ASCII art picture of the two options we're considering:
[Client]--| |--[DB]
[Client]--| |--[DB]
|--> [Web Service] --> [Business Logic] --> [Data Access] ----|
[Client]--| |--[DB]
[Client]--| |--[DB]
Or this:
[Client] --> [Web Service] --> [Business Logic] --> [Data Access] --> [DB]
[Client] --> [Web Service] --> [Business Logic] --> [Data Access] --> [DB]
[Client] --> [Web Service] --> [Business Logic] --> [Data Access] --> [DB]
[Client] --> [Web Service] --> [Business Logic] --> [Data Access] --> [DB]
Which one of these (or what in-between option) would be better and why?
This is a fairly general question, so I'll give a fairly general answer. I've built platforms on similar principles in the past and the only advice I can give you is to think carefully about dividing the architecture into two layers:
It maybe that many of your customers can operate on the generic framework alone, great, but when a customer is willing to pay you for some bespoking you can accommodate them through extension to, rather modification of, the generic layer.
In general we handled this kind of extensibility and coupling of generic with specialised behaviours through pretty standard techniques - a config file per customer that defines their processing 'pipeline', dynamic loading of customer assemblies, generous use of interfaces, allowing generic components to delegate operations to either standard implementations or customer specific implementations at runtime, and so on.
Hope that helps.