1

TL; DR

Given BDD documentation with features composed by several scenarios, should we:

  • Create a single interactor per feature
  • Create minor interactors per scenario, and composing the feature interactor

Details

We are trying to refactor a huge application and we are mapping the current (and future) features into BDD (behavior-driven development) stories with Gherkin language.

The tests methods signatures (and maybe even the interactor interfaces) will be automatically generated by test frameworks such as Cucumber and Calabash.

For example:

Feature: Transferring money between accounts
    In order to manage my money more efficiently
    As a bank client
    I want to transfer funds between my accounts whenever I need to

    Scenario: Transferring money to a savings account
        Given my Current account has a balance of 1000.00
        And my Savings account has a balance of 2000.00
        When I transfer 500.00 from my Current account to my Savings account
        Then I should have 500.00 in my Current account
        And I should have 2500.00 in my Savings account

    Scenario: Transferring with insufficient funds
        Given my Current account has a balance of 1000.00
        And my Savings account has a balance of 2000.00
        When I transfer 1500.00 from my Current account to my Savings account
        Then I should receive an 'insufficient funds' error
        Then I should have 1000.00 in my Current account
        And I should have 2000.00 in my Savings account

So, using Kotlin for example, we could have

class TransferMoneyInteractor {

    @Throws(IllegalStateException::class)
    suspend operator invoke(amount: Double, account: Account) {
        requireFunds(amount, account)
        transferMoney(amount, account)
        assertBalance(amount, account)
    }

    @Throws(IllegalStateException::class)
    private suspend fun assertBalance(amount Double, account: Account) = TODO()

    @Throws(IllegalStateException::class)
    private suspend fun requireFunds(value: Double, acc: Account) = TODO()

    @Throws(IllegalStateException::class)
    private suspend fun transferMoney(amount Double, account: Account) = TODO()

}

or have one interactor per operation:

class RequireSufficientAmountInteractor {

    @Throws(IllegalStateException::class)
    suspend operator invoke(amount: Double, account: Account) = TODO()

}

class AssertBalanceInteractor {

    @Throws(IllegalStateException::class)
    suspend operator invoke(amount: Double, account: Account)  = TODO()

}

class TransferMoneyInteractor(
    private val requireSufficienceBalance = RequireSufficienteBalanceInteractor(),
    private val assertBalance = AssertBalanceInteractor(),
) {

    @Throws(IllegalStateException::class)
    suspend operator invoke(amount: Double, account: Account) {
        requireSufficientBalance(amount, account)
        // .. do transfer operation
        assertBalance(amount, account)
    }

}

Considering clean-architecture, dependency injection, and BDD best practices, what would be the best approaches?

3
  • 1
    Which approach best meets your specific needs? Commented Nov 22, 2019 at 22:47
  • These "scenarios" are tests! Having 1 to 1 relationship between interactors and tests is madness. If you mean something else please edit. If you mean 1 to 1 just stop. Yuck. Commented Nov 23, 2019 at 0:49
  • @candied_orange assertBalance could be a simple assert, but requireSufficientBalance not.
    – JP Ventura
    Commented Nov 23, 2019 at 0:56

2 Answers 2

2

When applying Uncle Bob’s clean architecture principles an interactor is a “use case request handler”. In your example you have one use case, or feature as you call it. Therefore it makes sense to have a single interactor at this point.

At some point the requirements will change, for example the UI should display the ‘transfer funds’ button only if sufficient funds are available. At that point you should create a second interactor for the new use case.

Note that if you had the fund check in the first interactor you should extract the logic to prevent code duplication between interactors.

Ideally you start with another layer behind the interactors with a domain model; the interactors then basically become infrastructure that:

  • validates the request
  • loads necessary data
  • instantiates domain models and call their behavior
  • persists data
  • creates and returns a response
0

Is the intent for clients to only see a single interface? If so, either way is “correct”. Typically, you’d start with one class and as the API starts expanding you could add more “internal” classes to handle specific methods or groups of methods (your second example).

If the amount of methods starts becoming excessive or the methods being added feel like they do not belong together with the rest of the API you may want to segregate the interface into smaller ones.

Either way, as far as the amount of interactors is concerned, it does not really matter as far as it is not affecting the workflow of the teams working on it.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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