10

In Clean Architecture by Robert C. Martin the dependency rule points strictly from the outermost layer/ring to the innermost.

As an example a Dependency Injection Framework should lie on the outermost layer (frameworks) to allow for it to be replaced.

However, a DI framework which relies on attributes would clearly break this, as any class which would require those attributes would have a dependency on the framework. Therefore such a library could not be used following the dependency rule strictly.

I am running into the same problem with utility libraries, e.g. a Math Library or some Rx library providing IObservables/Subjects.

The math library could be wrapped by an adapter to keep it replacable - which makes sense, but for example a Entity providing the framework for both entities (inner most layer) as well as systems (business rules) and maybe even UI (presenters) simply does not go well with this design.

However, even for the math library the cost of adding the interfaces for Dependency Inversion+Adapter sounds pretty insane.

Am I missing something or is this more or less a rule which commonly break when trying to implement Clean Architecture.

3 Answers 3

10

Your observation is correct. However, that does not mean the "Clean Architecture" approach is wrong in general.

One major technique to decouple things from "outer rings" like the database layer or network layer from the business logic is Dependency Injection. This can help to make your system more decoupled from lots of technologies except one: the DI framework itself (in case you are using one). You cannot decouple a system from a DI framework by applying DI. If you want to avoid such a dependency, the only way is not to use any DI framework at all, and stick to Pure DI.

However, even for the math library the cost of adding the interfaces for Dependency Inversion+Adapter sounds pretty insane.

Yes, the benefits of abstraction and decoupling always come for a cost. So for each and every 3rd party library or tool or external system you are using, you have to evaluate the cost/benefit relationship of using it directly as "infrastructure" of your system, or if you should provide some abstraction layer.

A good litmus test are unit tests: can you create fast and simple unit tests for your software without an additional abstraction layer?

  • for a math lib, the answer will probably be "yes" -> decoupling may not be worth the hassle

  • for a database layer the answer will often be "no" -> decoupling is probably worth it

This approach may help you to decide for which parts of the system you stick to the "Clean Architecture", and for which parts you better ignore it.

1
  • Thank you. With the danger of beeing biased because you wrote what also is conform with my view on "Clean Architecture" I choose your answer as the important thing I value here the most is 'For each and every 3rd party library or tool or external system you are using, you have to evaluate the cost/benefit relationship of using it directly as "infrastructure" of your system, or if you should provide some abstraction layer'. Commented Jan 14, 2019 at 12:56
2

The point of the Clean Architecture is to make technology in the application easily replaceable. This comes at the cost of making changing business logic expensive.

So if you are doing an application in which changing business logic (i.e. delivering business value) is more likely than changing technologies, then I would suggest to just ignore the Clean Architecture ideas completely.

That being said, I'm not a fan of dependency injection frameworks either. It usually interferes with your design, so I would actually keep it out of the application entirely.

8
  • Thank you for the answer. I did not chose it as I think "ignore the Clearn Architecture ideas completly" is just as extreme as "follow them blindly" and I think there is a lot of value in many of those - which are clearly shared with other Architectures for the very same reason. Commented Jan 14, 2019 at 12:59
  • 4
    Saying Clean Architecture makes changing business logic more expensive is a complete misunderstanding of the architecture. Clean Architecture separates the business logic from the supporting technologies making both easier to change and grow. You're doing a disservice to the community by claiming this tradeoff.
    – Brad Irby
    Commented Jan 16, 2019 at 9:09
  • @BradIrby I get that you disagree and I welcome any discussions, but you'll have to come up with some points. I took the time to analyze Uncle Bob's own showcase project (linked above) to show that it is really difficult to change. Did you have a look at that? Do you see any flaws in the arguments? Commented Jan 16, 2019 at 10:20
  • "This comes at the cost of making changing business logic expensive." - I agree that this can be a result in case of overengineering, but it shouldn't. Changing the BL should ideally neither become harder nor easier
    – Doc Brown
    Commented Jan 17, 2019 at 10:07
  • @DocBrown Do you have an example project where it is done "right" in your opinion? I've analyzed Uncle Bob's own repo (javadevguy.wordpress.com/2017/11/15/screaming-architect), and it is pretty difficult to change. For a simple change it required changing 6 classes, and I had to track those down, compiler didn't help at all. Commented Jan 17, 2019 at 10:35
0

I solved the issue that I needed to create UUID objects and add validation on Entities doing these things below. As an example, I'll quote the UUID generation:

  1. I create a folder named shared at the root of the project. I added an IdFactoryProvider inside with an IdFactory as a static private attribute. I added two static methods inside: get (to return the factory) and changeImplementation (to be used only on test env. Internally it throws if it's not test env).

  2. On the infra layer I created an IdFactory protocol and the implementation UuidIdFactory, which calls UUID library internally.

With that I can:

  • Decouple my entity on domain layer from the external library.
  • Setup which factory will be returned only when app starts
  • Test easily the factory calling changeImplementation

The main reason for an entity to call directly a IdFactoryProvider by a static method and not receive this dep on the constructor refers to the amount of code that you need to create only to pass the provider for all entities on your system. It doesn't smell good.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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