0

Context

I'm currently developing an application following the Clean Architecture principles (at least I'm trying really hard to follow these).

All my Dependency Injections are done manually, without any Framework.

One of my Use Case consists of the user being able to answer to a question he was asked.

  • If he/she is wrong, nothing happens, he can try again

  • If he/she is right, a list of things happens :

    • I get back his/her current session, and modify the state of that session. (Update session step - DB read)

    • I get back a contact record from the database (Get Contact step - DB read)

    • I send a mail into an in-app mailbox with this contact information (basically it's a database insert of a message) (Send Mail step - DB write)

    • I save the updated session (Save session step - DB write)

    • I push an event into a custom Message Queue (Programm event step - Queue write)

Issue

All of the work described above are the steps I need to go through if the user answer to this question; it's kind of an Application Logic. For that reason, I created a specific AnswerQuestionUseCase which do all of this.

Because of all these databases interactions, I now have 5 dependencies injected into my Use Case, and I'm starting to think this is a code smell of bad design. The unit test of this class is ugly, not quite readable with all these mocks.

Solution ?

I searched a bit how to deal with this issue, and I found that Function Composition might be a good way to avoid so many dependencies, but I've found that if a go that way, all of the Application Logic would go outside of my Use Case, deported into the presenter maybe.

I could also break my Use Case into smaller Use Cases, but that just hide the issue, pushing the dependencies elsewhere.

I thought the Use Case was a block of Application Logic which you couldn't break apart for a specific use case of your app.

Is my understanding of what should be a Use Case any good ? How would you deal with that many dependencies ? How would you use Composition over DI to avoid so many mocks inside the unit test of this case ?

  • UseCase is totally agnostic to concepts like Session or Sending an Email if... or Program event. These things happen in different layers due to different reasons. Are not you, maybe, missing a service layer where to orchestrate whatever happens after the use case? Or a system of callbacks, error handling, etc. – Laiv Jun 27 at 13:01
  • Maybe you're right. Right now the Use Case manipulate these concepts via interfaces. What would the service you're talking about look like in this case ? I'm afraid about just moving some work at another level (service), then having nothing to do inside the UC but calling the newly created service. – Rafi Panoyan Jun 27 at 14:36
1

The simplest answer I know of is to separate the bits that consume the data from the bits that provide the data.

The bit that knows about the application specific business rules is just an in memory state machine -- aka an "object" -- with a data interface.

Separate from that is the protocol, the bit of logic that understands how to move the data from one place to another. This is the bit of code that coordinates the flow of data between the different dependencies. In other words, this code knows how to route the data from one place to another, but doesn't usually need to inspect the data.

So you test the application specific business rules by instantiating the object in your test, and passing data to it directly.

You test the protocol by using test doubles.

In some cases, the protocol can be broken down into logical units that don't involve all of the dependencies. In that case, you can write tests that focus on the smaller units, with tests that focus on the coordination between a subset of the dependencies, rather than all of them.

You might find these talks helpful:

  • If I understand, you wouldn't consider the protocol as application specific business rules ? My understanding about the Use Cases (application business rules) was that everything that had to be done for a particular use case is part of that, including the flow of data, since this flow must always be the same application-wise for this use case. – Rafi Panoyan Jun 27 at 12:48
  • 1
    everything involves too many things. "Answering a question" has nothing to do with sending an email if X or publishing an event if Y or updating a session of Z. Session, emails, logs, notifications can be executed as business side-effects instead of forcing them explicitly from the business itself. The business layers is not the application, the application is built upon the business. – Laiv Jun 27 at 13:32
  • Exactly, following the Clean Architecture principles (blog.cleancoder.com/uncle-bob/2012/08/13/…), Use Case is application layer, and Entity is business layer. If I understand your comment @Laiv, I can keep this side effects' flow in the Use Case (app layer) and create an Entity which will only validate the answer (pure function, no mocks). – Rafi Panoyan Jun 27 at 14:12
  • 2
    "the circles are schematic. You may find that you need more than just these four. There’s no rule that says you must always have just these four. However, The Dependency Rule always applies. Source code dependencies always point inwards." – VoiceOfUnreason Jun 27 at 14:56

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.