2

The dependency rule in Clean Architecture states that:

... that source code dependencies can only point inwards.

I now came across a problem that I cannot figure out myself.

Lets assume I have a module with application business rules abr and 2 modules for services that implement interfaces from abr.

           +---------+
           |   abr   |             Business Layer (defines interfaces
           +---------+             'Service1' and 'Service2')
      +----^         ^---+
 - - -|- - - - - - - - - |- - - - -
      |                  |
+-----+----+       +-----+----+
| service1 |       | service2 |    Dependencies (implement Interfaces 
+----------+       +----------+    define in abr)

Now the 2 services use the networking library OkHttp and should use the same user agent string for their requests.
Unfortunately I do not how to achieve this nicely.
Possibilities are:

  • Create a module sharedokhttpconfig that both service depend on
    This would violate the depdency rule
  • Put the code in abr
    This would violate that abr should not know about networking or even that the services use okhttp
  • Duplicate the code to set the user agent in both services
    Code duplication is bad

Another example would be that the project has 2 different UI modules that are fundamentally different but some logic in them would be the same.
Like formatting the address of a user.

Any suggestions on how to handle this?


To clarify why I think it would violate the dependency rule.

The abr is the middle of my application, it should not know about UI or anything else, so all dependencies must point int its direction.
But when I add a module that the 2 modules depend on it seems to me that they are pointing outwards.

           +---------+
           |   abr   |
           +---------+
      +----^ Inwards ^---+
      |                  |
+-----+----+       +-----+----+
| service1 |       | service2 |
+----------+       +----------+
      |     Outwards?    |
      +----v         v---+
           +---------+
           |  snbl   | snbl = share non-business logic
           +---------+

Or is doing it that way ok because from the perspective of abr nothing has changed?

  • 2
    Can you clarify for me how the dependency rule is violated? – larsbe Aug 22 '17 at 11:25
  • @larsbe I updated my question to hopefully answer your question. To clarify, it could be that this is actually not violating the dependency rule and I just got it wrong. I just added why I think it feels wrong – Altoyr Aug 22 '17 at 11:47
  • Like formatting the address of a user. String formatting is not a UI concern, unless you include terminal control characters or html tags and such in the string. – abuzittin gillifirca Aug 22 '17 at 12:00
  • Please stop using phrases like "clean architecture" to refer to one person's idea of general purpose recommendations. Clean architecture is a real term and should not be part of a cult. – Frank Hileman Aug 22 '17 at 23:06
  • @FrankHileman: any suggestion how else to refer to Bob Martin's article (if not by using the title, which is "Clean Architecture", if you like it or not)? – Doc Brown Aug 23 '17 at 18:55
1

After a discussion with the OP, we came to the following mutually shared conclusion:

The interfaces of both services are declared as part of business logic, while it's implementations are not part of it. Therefore, it makes sense if these implementations depend on configuration data for OkHttp.

The most promising solution for moving forward is to declare the module snbl, which includes the configuration data, in the same circle the service as implementations. Then a dependency between these and the configuration data for OkHttp is no violation of the dependency rule.

3

I'd just like to add something to the accepted answer, as well as to expand upon the insightful answer by Doc Brown. What's implicit in Uncle Bob's Clean Architecture is that "underneath" everything there is some common infrastructure that every circle depends on (e.g. the standard libraries provided with the language, and possibly some other components that you can choose to treat as standard).

So, this comes down to making a decision about what level of isolation you want for the OkHttp library. Let's assume for a moment that your "Dependencies" layer more or less corresponds to Uncle Bob's "Interface Adapters" circle; if you don't mind this layer/circle depending on OkHttp directly, then you can place snbl there and have it directly reference OkHttp.

enter image description here

If you would like, on the other hand, to avoid depending on OkHttp, then you can apply the exact same dependency inversion trick you used for your Business layer. This is actually what the insert at the bottom right depicts; but here's another image I found on the Internet that shows how the inversion happens:

enter image description here

Basically, what you do is you declare an interface that your "Dependencies" layer requires (in that same layer), and then you implement it in another layer that's "further out". This outer layer would roughly correspond to the "Frameworks & Drivers" circle; your snbl component would be implemented there, where it would, again, reference OkHttp directly.

In the end, I would like to acknowledge that this is (unless I'm terribly mistaken) pretty much what Doc Brown said much more concisely in his own answer (at least when it comes to key points).

2

You have basically two options:

  • either handle it the same way you do it with third party libs and frameworks: by using Dependency Inversion. You need to arrange some interfaces for "docking" the SNBL part of the application to the different services, so the services only depend on the interfaces, but not on the SNBL directly. If you like , you could place the SNBL part in a circle on its own, between the ring where you placed the services and the ring where OkHttp lives

  • or, you declare the SNBL to be part of the same circle where the services live. It may not be "business logic" in the strict sense, but it could be support functionality not needed somewhere else, so the "best" place may still be the business logic layer. That gives you the option to omit DI.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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