1
\$\begingroup\$

I am working in a Web Api project that has the following project structure:

Root
    Controllers
    Orchestrators
    Managers
    Services
    etc

The Controllers depend on Orchestrators.

The Orchestrators depend on Managers.

The Managers depend on Services. The Services are making calls to external dependencies e.g., third party APIs or data stores.

So for every flow, an incoming HTTP request follows this path:

Controller <--> Orchestrator <--> Manager <--> Service

Sample code:

AccountsController.cs

public class AccountsController
{
    private readonly AccountsOrchestrator accountsOrchestrator;
    public AccountsController(AccountsOrchestrator accountsOrchestrator)
    {
        this.accountsOrchestrator = accountsOrchestrator;
    }

    [HttpGet("GetAllAccounts")]
    public async Task<ActionResult<IEnumerable<string>>> GetAllAccounts(string input)
    {
        var accounts = accountsOrchestrator.GetAllAccounts(input);

        return new OkObjectResult(accounts);
    }

    [HttpGet("GetAccountById")]
    public async Task<ActionResult<IEnumerable<string>>> GetAccountById(string input)
    {
        var account = accountsOrchestrator.GetAccountById(input);

        return new OkObjectResult(account);
    }
}

AccountsOrchestrator.cs

public class AccountsOrchestrator
{
    private readonly AccountsManager accountsManager;

    public AccountsOrchestrator(AccountsManager accountsManager)
    {
        this.accountsManager = accountsManager;
    }

    public async Task<IEnumerable<string>> GetAllAccounts(string input)
    {
        // Validate input 

        if (string.IsNullOrEmpty(input))
        {
            return Enumerable.Empty<string>();
        }

        var accounts = await accountsManager.GetAllAccountsFromExternal(input);

        return accounts;
    }
}

AccountsManager.cs

public class AccountsManager
{
    private readonly ExternalDependency externalDependency;

    public async Task<IEnumerable<string>> GetAllAccountsFromExternal(string input)
    {
        // Validate input 

        if (string.IsNullOrEmpty(input))
        {
            return Enumerable.Empty<string>();
        }

        var accounts = await externalDependency.CallExternalService();

        return accounts;
    }
}

ExternalDependency.cs

public class ExternalDependency
{
    public async Task<IEnumerable<string>> CallExternalService()
    {
        
        await Task.Delay(500);
    }
}

There are more than a dozen Controllers which leads to more than a dozen Orchestrators and so on and so forth.

  • Is it good enough?
  • Is there a better approach for that?
  • Is there a pattern that specifically targets these scenarios?
\$\endgroup\$
0

1 Answer 1

2
\$\begingroup\$

Honestly, I'd advise against this.

Your approach seems to me a naïve view of how things work. Are there methods that just get data from a DB and return it? Sure. But a lot of the time business logic can be complicated and involve numerous database calls plus calls to external APIs etc. In such cases your structure quickly falls apart and will become an unmaintainable mess.

I see this far too often: people who have convinced them that "this kind of logic needs to be in a class named X to indicate that it is external" etc. That isn't maintainable. Nobody cares about that. (Instead, consider that an API which is used regularly is probably best encapsulated in a service which is then injected into wherever it is needed, and then the right method get called to retrieve the data you need or to post the data you need to send etc.)

My go-to approach is MediatR, where a method in a controller passes the incoming data to a specific Handler, which contains the business logic and often plenty of that logic is in specific classes in order not to have the Handler become unwieldy.

But YMMV, and there are other approaches equally valid (e.g. CQRS).

\$\endgroup\$

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.