19

Trying to apply Uncle Bob's clean architecture to an application I'm maintaining and I'm having difficulties with use cases and duplication/reuse. It's my understanding that usecases should be self contained and that duplication is to be expected but this does not seem to make sense with what I'm dealing with. I feel there are multiple use cases that should end up using other use cases.

Here is a very simplified example of 4 use cases that exist separately but that are also dependent on each other in the following way.

BulkSendUsersToUnit               
    SendUserToUnit   
        AddUserToMandatoryGroups  
            AddUserToGroup 

Each of them have validation, repository calls and conditions that always apply. Basically if I was doing this with services they would simply be methods that I would call from where I need them.

Can I/should I inject use cases in other use cases using DI?

What am I missing here?

Dont trust me
  • 199
  • 1
  • 1
  • 3

1 Answers1

25

A use case is not a method.
A use case is not an object.
A use case is not a layer.

A use case is a story about a user, using the software, in a particular case.

So it should come as no surprise that different use cases can reuse the same code.

But maybe you watched one of the video'spaywalled where Bob makes out like a use case is part of your architecture.

enter image description here

Well don't worry. It's just a name for the boxes in one of his layers. He's used other names for it:

enter image description here

Does this mean Uncle Bob is wrong? No. The Interactor / Use Case / Application Business Rule / Oh-pick-a-name-and-stick-with-it layer is where you ignore all of the needs of your application (details) and focus on the needs of the user going through a particular use case. But this location in your code is not THE use case. The use case is the whole story from when the user clicks a button to when they see the result.

So should interactors (or whatever you want to call them) use other interactors? Well this is the rub of following DRY to the extreme. You're not allowed to put the code from AddUserToGroup anywhere else right?

Baloney. If AddUserToMandatoryGroups means something different, has a different reason to want to change than AddUserToGroup does then it's ok to give AddUserToMandatoryGroups its own add-user code. Even if right now it's a character for character copy of the code in AddUserToGroup. If you have good reason to think these need to be able to change independent of each other it doesn't matter if right now they look identical. Apply DRY to repeating ideas. Not code.

As for Dependency Injection, that still works fine whenever you want to decouple what from when.

Bulk shitlist = new Bulk(annoyingUsers, bannedForLifeUnit);

Register.OnIveHadAllICanTake( ()-> shitlist.send() ); 
candied_orange
  • 102,279
  • 24
  • 197
  • 315
  • 3
    Nice code example. ;) – Robert Harvey May 01 '18 at 21:02
  • 9
    To follow Java naming conventions you should capitalize the "l" in "shitlist" i.e. shitList - oh wait!! Why is MY name in there!? – Greg Burghardt May 02 '18 at 03:01
  • 1
    Duplication is indeed fine if two use cases have different reasons to change. But what about similar use cases that have pretty much the same reason to change(e.g. `FollowUserUseCase` and `UnfollowUserUseCase`)? – Max Oct 31 '20 at 13:52
  • 1
    @Max reusing code can still be a good thing. But the more code is used the more care must be taken when refactoring it. – candied_orange Oct 31 '20 at 13:59
  • @candied_orange, I see. But what if no use case contains the shared logic, like in the case of follow/unfollow use cases? Does it make more sense to create a new use case that is only used by other use cases or keep the duplicate code? Maybe the best option is to create an object that contains the shared logic but not call it a use case (e.g., `FollowingUpdater`)? – Max Oct 31 '20 at 14:20
  • @Max if logic isn’t shared make your own logic. The use case is what you are supporting. It will both have unique logic and shared logic. – candied_orange Oct 31 '20 at 14:23
  • Say the use case involves scheduling the sending of an email at a later date. When we do send the email, for instance with a background worker, should we consider "sending the email" as a use case? I'm asking this because the latter "use case" would be "private", e.g. never triggered by the user directly. Is it still considered a use case then? – Konrad Feb 27 '22 at 14:01
  • 1
    @konrad A user subscribing to a magazine, getting monthly issues of it, and getting sent a "time to renew" email sounds a lot like a use case to me. – candied_orange Feb 27 '22 at 18:47
  • I agree, therefore a use case is not necessarily directly triggered by a user action, it can be an internal task. This is something I wasn't totally sure about (is a use case necessarily only callable by controllers that manage user requests, or can it be something callable by internal parts of the application), but it does make sense to me, thanks! – Konrad Feb 28 '22 at 12:43