We're currently in the early stages of building a substantial system, aiming to do much of what several existing systems do, in a flexible, extensible, maintainable way. It will support multiple complete instances for different customers. One of the things which keeps coming up across the whole system is: where should [component x] sit?
- (A) completely centralise it into a single separate web service, shared by all instances; configuration would be held centrally so that it could give each instance what it needed, and may include instance-specific plugins
- (B) have it as a separate service, but install it multiple times, configured individually for each main instance of the solution; the service might be used by several co-operating components within one instance
- (C) create the functionality as a library and incorporate it directly into the other components
For each there are some clear pros and cons:
A is resource-efficient, and easily allows e.g. monitoring of activity across the whole solution. Making a single service sufficiently configurable/flexible will create additional development challenges. Upgrades to the code might have an impact across the whole solution both in terms of down-time and testing/deployment planning, whether or not the code change is intended to improve every instance.
B uses more resources, but localises configuration, which may be less complex. Code changes can be rolled out in a more manageable way, but more effort will be required to do the roll-out in more than one place.
C removes the need for a separate service at all, and offers the potential for maximum flexibility if calling code doesn't have to be restricted to carrying out all functions via the web service API; upgrades to the code would require redeployment of DLLs everywhere they were used, which could be in every component of an instance rather than just once per instance as in B.
I'm specifically thinking about it from a functionality PoV not, worrying about e.g. where the data is actually stored. I've ignored option D, "copy and paste code all over the place, or reimplement willy nilly".
I'm wondering how it applies to a whole range of components, from specific business functions, to broader things like logging, error-handling, data-access, etc etc. I realise that the final answer will always depend heavily on each example, but are there considerations I haven't mentioned which could have a big impact? Are there examples where it's an absolute no-brainer? Is there some systematic way to help make this decision? Have I completely missed an entire option?
EDIT (a little more detail just in case it makes a difference): Historically, because of a lack of forethought, we have catered to the needs of several similar but also substantially different customers by basically forking the entire codebase apart from a handful of trivial libraries. We are definitely committed to some kind of SOA approach with the rewrite, and certainly a dramatically more shared codebase, but there may still be some benefit or requirement to maintaining some separation to each "instance", by which I mean an entire collection of services providing everything needed for one customer: the customer may require their own logically or physically or even geographically separate install; one customer's instance may consist of modules A and B, another B and C and D and E. I feel instinctively that there are sort of two ways to be "SOA" here: one to have literally one of everything, and let them all work together in different ways for different customers, or alternatively to have one of each of the needed modules within a customer, but the loosely coupled nature of the services would still allow you to add one more module to an existing customer easily, or move a resource-intensive module to its own hardware for only one customer.
Don't get me wrong, I appreciate all the points made, but I think I'm not quite in a mental place where merely the fact that doing something a certain way, which could definitely offer some benefit in some situations, could also lead to problems if managed poorly, is wholly unacceptable. So it's best just to not even let the possibility exist even if that leads to more complexity/effort/etc. Kind of like not allowing booze in the house, because you know that if it's there you might drink it all yourself and run out into the street in your underpants, even though a quiet drink to unwind after work might be nice. I should probably stop waffling now...