Single responsibility and feature envy in interactors

257 views
Skip to first unread message

Frederik Krautwald

unread,
Jun 27, 2014, 5:50:28 AM6/27/14
to clean-code...@googlegroups.com
A thing is puzzling my mind. When we have interactors, they often need to use the functionality of other interactors. Let me give an example.

We have an interactor which responsibility is to create a file, let’s call it CreateFileInteractor. The requirements are, however, that a file should only be created if it does not already exist. So, we introduce another interactor which can find a file, let’s call it FindFileInteractor. The execution method in the CreateFileInteractor then calls the FindFileInteractor’s execution method to determine whether the file already exists, and if it doesn’t, then the CreateFileInteractor continues its execution and creates the file.

Maybe it’s just me, but I feel, somehow, that the responsibility of checking whether a file exists still partly lives in the CreateFileInteractor, because the check is done there, even though the actual search is performed by the FindFileInteractor. Is it just me overreacting, or should some refactoring take place?

Sebastian Gozin

unread,
Jun 27, 2014, 7:13:19 AM6/27/14
to clean-code...@googlegroups.com
To me this feels like a precondition for executing the interactor in a specific context. I typically just call it input validation.
So I would declare that the file path must not already exist in order to execute the CreateFileInteractor.

This validation could then reuse the FindFileInteractor to determine this is true or not.

Now, I said "in a specific context" because sometimes different preconditions apply. So, I've noticed I may declare them separate from the actual interactor based on the path in the application we are following.
I find naming is a bit tough here because in practice what I have are composable functions or command objects and then usecases which consist of combining a possible composed function or command object with a set of validation rules and a topic.

The ui would always invoke a usecase and be subject to validation. While the composed functions or command objects would bypass this.
I'm not sure if this helps but when reading your post I felt like you were perhaps confusing functions with full blown usecases.

Frederik Krautwald

unread,
Jun 27, 2014, 7:29:59 AM6/27/14
to clean-code...@googlegroups.com
I am trying to follow UncleBob’s architecture, and he specifically said that the interactors are use cases and that use cases can be composed of other use cases (interactors). To me, in the light of UncleBob’s architecture, use cases / interactors are commands (which, of course, essentially are functions).

There has already been a discussion on where validation should live, and I believe the outcome was that if the validation method are prone to change in different contexts, it should live in the interactor (opposed to a self-validating entity).

Now, from your suggestion, I could see a higher level use case (interactor), which encompasses the CreateFileInteractor and FindFileInteractor. Naming is difficult, but along the lines of CreateFileIfNotExistInteractor (I need a better name), which then validates and creates.

Caio Fernando Bertoldi Paes de Andrade

unread,
Jun 27, 2014, 12:26:10 PM6/27/14
to clean-code...@googlegroups.com
Frederik,

Interactors are used somewhat directly by the users, so their names express what actions they perform and not how they perform them.

So a user would use a CreateFileInteractor, because that’s the requested action. The user doesn’t care about which specific validations will be made (contrast interactor name with CreateFileIfNotExistInteractor). Validations are specifics that change over time, you don’t want to change a public type name just because the implementation changed. ;-)

You could have a CheckFileExistsInteractor that checks if a file exists, but that interactor wouldn’t be public to the users as an action, instead it would be a sort of private interactor, helping other interactors in how to perform the actions.

Maybe what you missed in Uncle Bob’s explanation is this difference between the public and private (or user and helper) interactors. :-)

Hope this helps,
Caio

--
The only way to go fast is to go well.
---
You received this message because you are subscribed to the Google Groups "Clean Code Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clean-code-discu...@googlegroups.com.
To post to this group, send email to clean-code...@googlegroups.com.
Visit this group at http://groups.google.com/group/clean-code-discussion.

Frederik Krautwald

unread,
Jun 27, 2014, 12:32:49 PM6/27/14
to clean-code...@googlegroups.com
Indeed. To sum up. The "private" CheckFileExistsInteractor will still be called from CreateFileInteractor, but it won’t implement a public boundary interface. Is that correctly understood?
To unsubscribe from this group and stop receiving emails from it, send an email to clean-code-discussion+unsub...@googlegroups.com.

Caio Fernando Bertoldi Paes de Andrade

unread,
Jun 27, 2014, 12:54:20 PM6/27/14
to clean-code...@googlegroups.com
It has to conform to the Command Pattern just like any other interactor, conforming to the OCP, but the delivery mechanism wouldn't call it directly, only other interactors would. What you can do to try to make it explicitly "private" is protecting it inside a package, but that trumps reusability a bit. Apart from that, nothing really prevents the delivery mechanism from using them other than convention, actually. That's one reason why this is a bit confusing at first.

Sent from my iPhone
To unsubscribe from this group and stop receiving emails from it, send an email to clean-code-discu...@googlegroups.com.

Łukasz Duda

unread,
Jun 27, 2014, 2:21:51 PM6/27/14
to clean-code...@googlegroups.com
"Create file" IMHO is not a good example of use case. I'd use some file utility, as a dependency of use case, but use case should cover some business logic. File sounds like implementation detail. Requirements should not suggest implementation.

Frederik Krautwald

unread,
Jun 27, 2014, 2:38:35 PM6/27/14
to clean-code...@googlegroups.com
Yes. It is actually CreateAccountingFileInteractor, but for brevity, I left out the accounting part. As such, it is not necessarily a file as a file on a disk, but more like a collection of related data. 

Łukasz Duda

unread,
Jun 27, 2014, 3:05:51 PM6/27/14
to clean-code...@googlegroups.com
What kind of data does accounting file contain?
Maybe it's supposed to be in AccountingGateway implementation, which uses some kind of file utility?

Frederik Krautwald

unread,
Jun 27, 2014, 3:10:36 PM6/27/14
to clean-code...@googlegroups.com
There is a gateway which is passed to the interactor. My concern was regarding principle of responsibility and feature envy in use case interactors. An accounting file is business logic. It contains organization name, date of creation, first month of fiscal year, transactions, etc. It is the entry point for storage of financial data in accounting.

Łukasz Duda

unread,
Jun 27, 2014, 3:47:07 PM6/27/14
to clean-code...@googlegroups.com
It looks to me like accounting department wants to store some data. How would you name it? Is it summary of fiscal year in context of some organization's transactions?
I'd find the data first. It can help to find their abstraction, and create the entities.
The file still looks to me like a job of gateway to store entities data. Tell me if I'm wrong.


--

Caio Fernando Bertoldi Paes de Andrade

unread,
Jun 27, 2014, 4:22:51 PM6/27/14
to clean-code...@googlegroups.com
Be careful about trying to model data based on what your customer thinks she needs, that’s the source of many over-engineered systems.

Asking about the use cases first and building the data model as the application grows is a much simpler and more effective approach.

If your use cases involve some sort of File, find a clearer name (AccountingFile or maybe AccountingRecord), avoiding confusion with our technical computing science definition of File, a very good example of such confusion just happened. :-)

Caio

Frederik Krautwald

unread,
Jun 27, 2014, 6:06:40 PM6/27/14
to clean-code...@googlegroups.com
The accountant talks about an accounting file for an organization. It is her domain language. She needs to record transactions for different organizations with each their chart of accounts. So to keep transactions coupled with each organization, an accounting "file" is the "identifier" or "container". However, it is true in a sense that it might not be needed per se as an entity, but we need some way of storing the organizations name, it’s legal name, it’s first month of fiscal year, the accounting creation date, tax form, etc. This is what the accountant refers to as an accounting file.
To unsubscribe from this group and stop receiving emails from it, send an email to clean-code-discussion+unsub...@googlegroups.com.

Frederik Krautwald

unread,
Jun 27, 2014, 6:08:33 PM6/27/14
to clean-code...@googlegroups.com
Indeed. See my answer to Łukasz above.

Caio

To unsubscribe from this group and stop receiving emails from it, send an email to clean-code-discussion+unsub...@googlegroups.com.

To post to this group, send email to clean-code...@googlegroups.com.
Visit this group at http://groups.google.com/group/clean-code-discussion.

--
The only way to go fast is to go well.
---
You received this message because you are subscribed to the Google Groups "Clean Code Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clean-code-discussion+unsub...@googlegroups.com.

unclebob

unread,
Jun 27, 2014, 10:22:41 PM6/27/14
to clean-code...@googlegroups.com
Ivar Jacobson's lovely book "Object Oriented Software Engineering: A Use Case Driven Approach" addresses this concern directly.  Ivar showed that use-cases (what I call interactors) can contain each other, and derive from each other.

A use case can be composed of smaller use cases that it contains.  When implemented as an interactor this typically means that the interactor uses some kind of context or session token to know what state the interaction is in, and therefore how it ought to respond to incoming requests.  In your CreateFileInteractor example, the CreateFileInteractor might invoke the FindFileInteractor and give it a special 'CreatingFile' token.  The FindFileInteractor would do it's normal job and eventually invoke a controller with the name of the found file, if any.  It would also pass that controller the 'CreatingFile' token.  The controller knows that token means it should pass it's 'fileFound' message to the CreateFileInteractor, which will then return with an error message if the file already exists.

This 'token' method is just one of the many ways that you can use to allow interactors to invoke each other.

You can also inherit use cases.  This is done by creating an abstract interactor that implements the Template Method pattern.  Derivatives of the interactor implement it's various operations differently.  

Jakob Holderbaum

unread,
Jun 28, 2014, 12:22:36 AM6/28/14
to clean-code...@googlegroups.com
Hi Frederik,

I had a similar discussion with a co-worker the other day. My personal
preference is to not reuse Interactors inside of other Interactors. I
see them as exposed interfaces that are highly exposed to interface
changes even through simple requirement changes. By using them
internally, you expose your other Interactors to probably irrelevant
changes (and recompilations).

But still, there is this problem of the need for logical reuse! So, I
tend to build internal services, which are used by Interactors. So the
reuse can be done by using the same service on several Interactors. I
see it like a push down of the logic from the actual Interactor to the
hidden layer below (e.g. the outer ring of the hexagon, if you are into
hexagonal architecture).

In your specific example of AccountingFiles, I would go for a
AccountingFileGateway that gets injected in your Interactor instances.
This Gateway will surely have some methods for retrieval of existing
AccountFiles and the storage of newly created ones (by a Factory).

So, I don't see the need to reuse Interactors in your specific example.

WDYT?

Cheers
Jakob
--
Jakob Holderbaum, B.Sc
- Systems Engineer -
phon 0176 637 297 71
mail h...@jakob.io
page http://jakob.io

Frederik Krautwald

unread,
Jun 28, 2014, 5:01:49 AM6/28/14
to clean-code...@googlegroups.com, mail...@jakob.io
I like your idea of pushing the logic down to internal services.

An AccountingFileGateway is injected in the interactor, but the gateway is not responsible of the business logic, that is, whether to overwrite a file or not. The gateway simple does what it’s told.

Jakob Holderbaum

unread,
Jun 28, 2014, 6:49:00 AM6/28/14
to clean-code...@googlegroups.com
Of course you don't have to implement domain logic inside of the gateway.

Bu I tried with a lot of success an interesting separation in my
last/current project.

Instead of keeping the Gateway especially dump so that it can be
reimplemented, I went down a different road:

+---------+ +---------+ +---------+
-Entity-> | Gateway | -E-> | Marshal | -DTO-> | Backend |
+---------+ +---------+ +---------+

+---------+ +---------+ +---------+
<-Entity- | Gateway | <-E- | Marshal | <-DTO- | Backend |
+---------+ +---------+ +---------+


So, the Gateway holds an injected Marshal and an injected Backend. The
Marshal resides somewhere in the domain world. He is specifically
responsible for transforming known Entities in storable DTO and vice-versa.

The Backend is a interface which only operates on the DTO, no Entities
are involved here.

By this approach, you "push-down" the stupid simplicity of persistence
into a Backend implementation. The Gateway can now hold a very very low
amount of business-related information.

I had some situations where domain-agnostic gateways felt very stupid.
This is one of the solutions I came up with.

We tried another approach by encapsulating the Gateway in a
Domain-Service which encapsulated specific behavioural facts around
storage and such.

WDYT?
>> mail h...@jakob.io <javascript:>
>> page http://jakob.io

Christian B

unread,
Jun 28, 2014, 7:53:42 AM6/28/14
to clean-code...@googlegroups.com
This answer is great. - 2 more questions i had :)
Reply all
Reply to author
Forward
0 new messages