1

Imagine a set of libraries that represent some APIs. By using an inversion of control mechanisms, concrete implementations will be injected in a consuming project.

Here is a situation. I have some of the API libraries depending on other API libraries for certain functionalities - therefore the API libraries themselves are coupled at some point. This coupling can become an issue later, because changing one API will result in changes of the dependent APIs, and the corresponding implementations will also need to be changed, so in the worst case we end up with quite a number of projects that need to be modified to reflect a change form only one of them.

Now I have in mind two possible solutions for this:

  • Create a monolith API project that unites the related API libraries.

  • Further decouple APIs by making each library provide interfaces for all functionalities that are dependent on the other API, so the direct dependency is removed. This might result in a similar code in both libraries, but gives freedom to the implementations chosen via the IoC mechanisms and also allows the APIs to improve independently from each other (when an API is changed, the changes would affect only its implementation libraries, not other APIs or their implementatons).

The problem with the second approach is the duplicating of code, and the result might be of having too much api libraries that need to be referenced (for instance, in .NET application each API will be a separate DLL. In some scenarios, like Silverlight applications, this can be an issue with app size - download time and client performance overally).

Is there a better solution for the situation. When is it better to merge some API libs into one bigger and when not? I know this is a very general question I am asking, but lets ignore the due dates, estimations, client requirements and technologies for a moment, I want to be able to determine the right approach based on both achieving maximum scalability and minimum maintanance time. So, what could be a good reason to choose either approach, or another one you might suggest me?

Edit:

I feel like I must clarify something about the question. I have in mind decoupling APIs from each other, not the API from its implementation. So, for instance if I have security API for validating permissions of access, and user accounts API that uses (references) the security API, changing security API will bring the need of changing the user accounts API, and the implementations of both of them. The more APIs that happen to be coupled this way, the more changes will have to be applied. It is what I want to avoid.

Ivaylo Slavov
  • 8,839
  • 12
  • 65
  • 108

3 Answers3

5

The choice is between few huge libraries and a myriad of small libraries.

  • If you have a huge library, the code within will tend to be tightly coupled simply because there's no force providing pressure to design the various elements in a loosely coupled way. The risk is that it becomes harder and harder to evolve that library because there are so many interdependencies that must be coordinated. Think about the .NET Base Class Library as an example.
  • If you have a myriad of small libraries, you might risk dll hell. Yes, we were promised many years ago that this was over, but it's not. Just try to consume a lot of fine-grained open source libraries in your application code base and you'll know what I mean.

Still, the Single Responsibility Principle also applies at the package level, so I'd recommend small, focused libraries instead of huge general-purpose libraries. This also makes it easier to always pick best-of-breed libraries.

Small libraries can always be composed/compiled into larger libraries (in .NET with an Assembly Linker / Merger / Repacker utility), while it's much harder to split a big library.

No matter what you do, the most important thing to keep in mind is backwards compatibility. The fewer breaking changes you introduce, the easier those libraries will be to manage.

Ruben Bartelink
  • 59,778
  • 26
  • 187
  • 249
Mark Seemann
  • 225,310
  • 48
  • 427
  • 736
1

I don't see this as a problem, really. Some library will depend on other libraries, and this is fine to me: improving one library will improve all the dependents! The "owner" of a library will have the responsibility not to break existing code, when making a change, but this is normal and can easily be handled if the code is well designed.

vulkanino
  • 9,074
  • 7
  • 44
  • 71
  • It is true what you say. Still being in position of the libraries owner, and tending to use them in more that one project, will cause things to become very complicated when applying changes. Having too much code to be modified is an issue I'd like to avoid. Also, versioning of libraries implies branching them. Finding a problem valid for more than one branch causes modifications of all of them, not to mention dependent projects and implementations. I'd be glad to learn or be adviced on ways to minimize these efforts. – Ivaylo Slavov Feb 02 '12 at 14:28
  • If you glimpse in my second approach, you'd probably see that it attempts to minimize the targets of a change, refactoring or update, at the cost additional complication and maybe duplication of code. Still, improvements to all related libraries will be at hand without modifying all of them. My point is to both minimize the dependencies that will need modification and updates, and at the same time allow the benefits of the changes to affect as much of the consuming app as possible. – Ivaylo Slavov Feb 02 '12 at 14:31
  • I disagree, your "solution" to a non-problem could make things harder because there's nothing to solve. Dependencies should be resolved by a tool, or something "external", not by code! This is not decoupling, it looks like an antipattern to me. My opinion, of course. – vulkanino Feb 02 '12 at 14:40
  • I think you misunderstood me, I want to decouple APIs (API libraries) from each other, rather the API and implementation. I have IoC for that. My question is backed with the negative experience our team is having by using a lot of API libraries, that depend on each other.Refactoring becomes a pain, maintenance of both libraries and projects which use them is becoming more and more time consuming. I don't even think of making further changes to that not-so-good situation, but I want to avoid it in the future, when we abandon the current code and re-design our APIs from scratch. – Ivaylo Slavov Feb 02 '12 at 14:47
1

If you have changes rippling through all dependent code you should reconsider your design. If your library surfaces a certain API it should isolate its consumers from changes to underlying classes or libraries.


Update 1:

If your application uses Library1 with API1 it should not have to deal with the fact that Library1 uses Lib2, Lib3, .. , LibX.

E.g. The Moq mocking library depends on CastleDynamicProxy. Why should you have to care about that? You get an assembly where DynamicProxy is already merged in and you can just use Moq. You never see, use or have to care about DynamicProxy. So if the DP API changes, that would not affect your tests written using Moq. Moq isolates your code from changes in the API of the underlying DP.

Update 2:

Finding a problem valid for more than one branch causes modifications of all of them

If that is the case you don't build a library but a helper for a very specific problem that should NEVER be forced upon other projects. Shared libraries tend to degenerate to a collection of "might be useful somewhere in the distant future". Don't! This will always bite you in the a**! If you have a solution for a problem that occurs in more than one place (like Guard classes): share it. If you believe that you might find a use for some solution to a problem: leave it in the project until you really have that situation. Then share it. Never do that "just in case".

Sebastian Weber
  • 6,766
  • 2
  • 30
  • 49
  • Do you mean something like `API1` providing its own interfaces for functionality of `API2` in the places where `API2` is used in `API1`? – Ivaylo Slavov Feb 02 '12 at 14:34
  • I have revisited my question because I believe it is misleading. I want to decouple APIs depending on each other, rather the API and implementation. IoC containers are very good ad the last one, that I never doubt. – Ivaylo Slavov Feb 02 '12 at 14:56