2

Background

In Uncle Bob's Clean Architecture, use case interactors are responsible for the orchestration of business objects to accomplish some user goal. As an easy example, an e-commerce application might have a use case to purchase items in a shopping cart: the interactor receives a request (DTO) to make a purchase, then the interactor might query various output ports (gateways) for instance to check inventory/availability, check to see if a payment can be made, and if so, persist a change to the inventory service, ending with a response (another DTO) indicating the success/failure of the interaction (the user-interface layers then presents this information to the user).

In trying to apply the principles of Clean Architecture to my applications, I continually run into the question of whether the use case interactor and gateways should be implemented on a front-end, back-end, or both. Consider the scenario for a mobile phone app that communicates with a web app - in a scenario like this, I've tended to notice that implementing use case interactors in the front-end lead them to be very anemic. That is, they only relay requests from the user-interface (typically a function call made on a view-model) to an output port (gateway interface). The gateway is good as it provides some level of abstraction about how communication with the back-end occurs (HTTP, sockets, IPC, RPC, etc), a detail that Uncle Bob stresses to be kept open for as long as possible. But I question the value of a use case interactor that merely relays requests - after all, why shouldn't a view-model depend directly on the output port? This is especially the case when the critical business rules must guarded by executing on an external device (e.g. web server, hardware, etc.) to prevent bad or unauthorized requests.

My typical attempt usually ends up as below. The output port provides a separation from the specific mechanism for sending a request. The only problem is that the use case interactor seems very anemic as mentioned since it merely relays a request to the gateway because the interesting business logic occurs on the external device or web app. enter image description here

Consequently, I'm wondering if I should go with the following approach instead, but I'm not sure what the advantages/disadvantages of removing the use case interactor - it seems improper to depend from one adapter directly to another another adapter (albeit via an abstraction).

enter image description here

Now, in this circumstance, I do not care how external server, hardware, or web app are implemented so long as I obey it's API contract. Though, supposing I was responsible for implementing the external application, is this where the use case interactor starts to be useful as now I have critical business rules and user flows that require orchestration?

My current conclusion is that it would seem that in a front-end/back-end system, the front-end would only be interested in use case interactors insofar-as the interesting business rules occur only within that tier (i.e. the front-end). If there are interesting business rules that cannot be fully executed in the front-end, perhaps due to security/authorization requirements, then we'll have to move that use-case interactor to the back-end tier instead and can simply have a view-model depend directly on the output port as in the second diagram.

Question

Does anyone have an expert opinion regarding tiering, MVVM, and Clean Architecture who can show me where I might be going astray? I'm hoping to understand:

  1. Is it useful (perhaps for maintainability reasons) in having use case interactors that only relay to a gateway (because the sophisticated business logic occurs on a different tier).
  2. What harms, if any, are there in depending directly from a view-model (an entry-point adapter) to an exit-point adapter via some output port interface?
1

A mobile e-commerce app that talks to a web service is unlikely to have much business logic in the app itself. In this case, it would be overkill to apply clean architecture independently to the app. Rather, you could consider the app to be the outermost layer of your overall architecture, which talks to the web service, which in turn talks to the interactors that contain the real business logic.

All you need in the app is the MVVM portion. The "model" is likely just the DTOs returned by the web service, and the view-model can handle presentation logic and interactions with the web service gateway.

| improve this answer | |
1

I have never tried to strictly apply Clean Architecture, nor do I think Uncle Bob intended it as a strict rule book. Don't write code that you don't need yet.

In the case your use case would have no logic on its own, I would just leave it out, like you described. This doesn't feel like a violation of any Clean Architecture. You could see it like you Output port and Input port become the same and your interactor just fizzles away. If your future requirements change, you can always create an interactor that implements the existing interface both as input and output port.

E.g.: You might have a view to subscribe to a newsletter entering an email address. You there is an API call to make that request and the backend will check the validity of the email and return either success or why it failed (maybe there was no "@" in the string, or the provider is blacklisted). Your interface is just a method "set_password" taking a string, return null or an error string.

As a future requirement you want the validation to be client-side for simple validations. So you create an interactor that knows the api-controller and implements "set_password" by implementing the syntax check and return an error message on failure and delegate to the api-controller on success.

In any case, you want to make sure that the methods making the web API calls are not doing much more, because making web API calls is hard to test. (See humble object pattern).

| improve this answer | |

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.