Clean Architecture - Robert Martin - Use Case Granularity
Asked Answered
J

2

8

I am considering implementing Robert Martin's Clean Architecture in a project and I am trying to find out how to handle non-trivial use cases.

I am finding it difficult to scale the architecture to complex/composed use cases, especially use cases where the actor is the system as opposed to a user, as in system performing some sort of batch processing.

For illustration purposes, let's assume a use case like "System updates all account balances" implemented in pseudocode like

class UpdateAllAccountBalancesInteraction {
    function Execute() {
        Get a list of all accounts
        For each account
            Get a list of all new transactions for account
            For each transaction
                Perform some specific calculation on the transaction
            Update account balance
    }
}

In addition, "Get a list of all accounts", "Get a list of all new transactions for account", "Perform some specific calculation on the transaction", "Update account balance" are all valid use cases of their own and each of them is already implemented in its own interaction class.

A few questions arise:

  • Is the use case "System updates all account balances" even a valid use case or should it be broken down into smaller use cases (although from a business prospective it seems to make sense, it is a legitimate business scenario)?
  • Is UpdateAllAccountBalancesInteraction a legitimate interaction?
  • Is an interaction allowed to/supposed to orchestrate other interactions?
  • Is code that orchestrates other interactions really belonging somewhere else?
  • Is it just OK to have UpdateAllAccountBalancesInteraction as an interaction, but have it call functions shared by the other interactors rather than act as an orchestrator of other interactors?
Jeepers answered 29/1, 2016 at 15:51 Comment(0)
B
2

Clearly, you have a new for high level interactions that share some (or a lot of) common functionality with lower level interactions. This is ok.

If the business requires a use case called UpdateAllAccountBalances, then it is a valid use case, and it's good that you're naming it in a way that reflects the business logic.

It's o.k. for one interaction to call other interactions, if this reflects your business logic accurately. Ask yourself the following question: If the requirements for UpdateAccountBalance change, should this also affect UpdateAllAccountBalances in exactly the same way? If the answer is yes, then the best way to achieve this is to have UpdateAllAccountBalances call UpdateAccountBalance, because otherwise, you'll need to make a change in two places in order to keep them consistent. If the answer is no, then you want to decouple the two interactions, and this can be done by having them call shared functions.

Boric answered 29/1, 2016 at 18:34 Comment(1)
I had to make up my mind and move forward with my project, so I concluded that the examples provided in the presentation/article are trivial and that the dependency separation refers to the layers, I think that is easy to agree upon. With that in mind, I concluded that one interaction's Execute() method should not call another interaction's Execute() method, but they can definitely share code in the same layer.Jeepers
P
0

My suggestion is to approach the problem differently. Represent the problem itself in a domain model, rather than using a procedural approach. Your seeing some of the problems with Use Cases, one of which is that their granularity is generally indeterminate.

In a domain model, the standard way to represent a specific thing (i.e. an "account") is with two objects. One representing the specific account, and an associated object representing those things common to all accounts.

AccountCatalog (1) ---- (*) SpecificAccount

In your example, SpecificAccount would have a service (method) "UpdateBalance". AccountCatalog has a service (method) "UpdateAllBalances", which sends a message UpdateBalance to all SpecificAccounts in its collection.

Now anything can send the UpdateAllBalances message. Another object, human interaction, or another system.

I should note, that it can be common for an account to "know" (i.e. maintain) its own balance, rather than it being told to update.

Privilege answered 31/1, 2016 at 8:8 Comment(1)
The Clean Architecture approach suggested using a domain model as well, just as you described it. That is a domain model where entities and enterprise business rules are defined, with no dependencies on anything else. In addition, it suggests a layer above the domain model, where the application business rules are implemented, and implementing use cases as classes relying on an interface similar to a Command Design Pattern one. My question was about composition in this last layer.Jeepers

© 2022 - 2024 — McMap. All rights reserved.