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.

Doc Brown
  • 199,015
  • 33
  • 367
  • 565
Kevin Streicher
  • 371
  • 1
  • 3
  • 9

3 Answers3

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.

Doc Brown
  • 199,015
  • 33
  • 367
  • 565
  • 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'. – Kevin Streicher Jan 14 '19 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.

Robert Bräutigam
  • 11,473
  • 1
  • 17
  • 36
  • 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. – Kevin Streicher Jan 14 '19 at 12:59
  • 3
    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 Jan 16 '19 at 09: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? – Robert Bräutigam Jan 16 '19 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 Jan 17 '19 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 (https://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. – Robert Bräutigam Jan 17 '19 at 10:35
  • When used properly, I can build my entire domain model with all business logic inside, using only unit tests. I don't need databases or external interfaces or UI. I can then add the infrastructure elements and other things necessary to make it a functional application. If the infrastructure and domain model affected each other as much as you claim, this would not be possible. As long as business logic changes don't affect the Interfaces used to interact with infrastructure elements, I can change the logic as much as I like without consequence. – Brad Irby Jan 18 '19 at 13:15
  • Similarly with the infrastructure - i can change my DB or other supporting services without affecting any business logic in the domain. Having to change 6 classes for a simple update does not sound like the fault of the architecture, but how it's implemented. When I need to make a change where I send in additional info to domain logic (which requires more code than simple business rule changes) I simply change the DTO that carries params to my command, and update the domain object to implement the new logic. Of course, this ignores UI changes. – Brad Irby Jan 18 '19 at 13:21
  • @BradIrby This is the same narrative EJB 1 was sold on. I just don't see it in practice, and I'm quite frankly skeptical whether it is true, whether these kinds of technical-based architectures really work. Again I would just ask: do you know of any particular (public) project that is in your opinion a fair (not necessarily perfect) representation of what you're saying? – Robert Bräutigam Jan 19 '19 at 07:34
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.