3

Clean Architecture defines level as a distance from inputs and outputs. So Entities or business-objects are the highest level.

What is a practical reason to make lower level dependent on higher level?

To me it looks like the frequency of change must determine the direction of a dependency. And it is not guaranteed that higher level will change less frequently. And assuming that we should depend on abstractions, it looks not so important to which level an abstraction belongs and how dependency is oriented. What do you think?

6
  • 2
    o me it looks like the frequency of change must determine the direction of a dependency I am not sure I agree but I am not sure I understand. Can you elaborate on why you believe this?
    – John Wu
    Commented Jul 15, 2020 at 22:43
  • 1
    Your misconception seems to be about the meaning of dependency. We speak of a dependency when one piece of code (A) calls another piece of code (B). Then A depends on B. B may be unaware of A, it does not need A to run thus it does not depend on it. Change frequency has nothing to do with this. Commented Jul 16, 2020 at 6:39
  • @MartinMaat why do you think there is a misconception? I clearly understand what dependency is :) Commented Jul 16, 2020 at 7:36
  • @JohnWu I will explain. If A depends on B then when B changes A will be also changed (of course it is oversimplified and over-generalized but this is how they explain it in books :) this is so because A knows about B and B does not know about A. I think it is valuable to organize your code so that a change that you make affected the rest of the code as little as possible. So unstable often changing code must depend on more stable and not vice versa. And this all is about stability and not about a level. Looks like B. Martin assumes that higher levels are more stable... Commented Jul 16, 2020 at 7:47
  • @Anton Petrov "If A depends on B then when B changes A will be also have changed" It is getting more confusing. First, I would not call that a change in A. More importantly, the direction of dependencies are determined by the real world your model is supposed to represent, it is not something for you as a designer to choose based on what seems convenient for whatever practical reason. So... NO! Commented Jul 16, 2020 at 8:36

1 Answer 1

3

There are two perspectives to consider here. The first is that we want to isolate (to the extent that is feasible) the higher-level policies (in this case, the "core" of the application, or the domain model, the thing that solves the core problem) from considerations that come from lower-level libraries/frameworks/tools that we use, but that aren't essential to the core problem itself. Of course, some aspects of the low-level details cannot be ignored for practical reasons, but generally speaking, some amount of isolation can be achieved. You might consider that a bird's-eye view of things.

The second perspective is one that presents a closer look at layer boundaries. You control dependencies between layers (and often within layers) by having the lower layer depend on an abstraction in a higher-level layer. It is these abstractions1 that need to be comparatively stable (change less frequently); the rest of the layer is hidden behind them.

[lower-level layer]-------->[higher-level layer]

is actually:

[low-level detail]---------||----->[abstraction]<-------[high-level policy]
                           ^          ^
               (layer boundary)     (abstraction is owned by higher-level layer)

So, as you're developing, you'll recognize the more stable aspects of the problem and codify these into things like input and output ports2 (ports and adapters style), or other kinds of inter-layer interfaces, and then you'll be able to refactor and restructure behind those. Since at the start you have limited understanding of the domain and of the forces of change involved, chances are that you'll have to revise some of these abstractions as you go along, at certain points; but if things are going right, they should stabilize further over time. That said, it's not advisable to invest the same amount of design effort in all parts of the system (there's a cost/benefit tradeoff to be considered); but for those parts that experience the most change and the most activity, you want to get to something that's reliant on stable abstractions and is fairly open to the kinds of changes that are most likely3 (open/closed principle).


1 By abstraction, I don't mean just an abstract class or an interface type (a la C#, Java); I'm using the word in a broader sense. E.g. a Facade to a subsystem or a component is also a kind of an abstraction.

2 Again, these aren't necessarily (C#, Java) interfaces. Initially, it may not even be a separate element; it could just be the public interface (public methods and properties) of a class. But that might change as the system becomes more complex; e.g., at some point you might do something like refactor the code to extract a new class (or two) out of the original one. The new classes then become internal to the layer, with the original one serving as a kind of a boundary object.

3 You're not supposed to guess what's most likely, although you might be able to do so, to some extent. Instead, this comes over time, with the growing understanding of the domain - as long as you consciously take note of what is actually going on as the project evolves.

5
  • I agree with that but this does not answer a question. Why [abstraction] is owned by a higher-level layer and not by the lower level (you could put layer boundary on the opposite side of the abstraction)? Why at all is this important? Commented Jul 16, 2020 at 7:55
  • 1
    @AntonPetrov - ok, the way you phrased it, I think part of your question was about the apparent contradiction between the need for stable abstractions and the fact that inner layers weren't necessarily more stable (and this is resolved by the idea that these instabilities can be hidden behind the objects at the boundary - the outer layers don't "see" the instabilities, as long as they don't change the interfaces). So hopefully there's some value to the answer. As for why the higher-level layer owns the abstraction - 1/2 Commented Jul 16, 2020 at 10:44
  • 1
    @AntonPetrov (continued) - it's because this lets the lower-level layer (or its parts) be pluggable (logically or physically - think about separating them into replaceable DLLs or JARs). This kind of structure lets you do things like swap components with mocks for testing, try different libraries and algorithms, or extend the capabilities of the system while localizing the impact of changes. Is that closer to what you had in mind? Or are you interested in an even more high-level view - i.e. why put the higher-level policy in the center and have everything depend on it? 2/2 Commented Jul 16, 2020 at 10:44
  • 1
    While we use abstractions everywhere at boundaries I agree that it does not really matter which layer is more unstable inside in its details. Thinking a bit about your second part I see that this is because the meaningful core of our application (the business-logic) is the reason why we create this software and is the most immovable part while everything else around it is as you said pluggable. But "immovable" does not mean "stable" but this is how it works. 1/2 Commented Jul 16, 2020 at 13:10
  • Outer layers are servants and the change propagates from inside - when the master (business logic) changes - servants must obey and must change too and not vice versa. I am still curious what is your insight on "why put the higher-level policy in the center and have everything depend on it?" Thank you for your time! 2/2 Commented Jul 16, 2020 at 13:10

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.