0

I am trying to implement a layered architecture consisting of; Controller, Service, Repository, Entity.

Each module has at least some of the above layers.

I am often finding that modules need to share some functionality, for example when creating a BlogPost I may need information about the Account, currently I am injecting the services into the controllers, which works fine.

However my question comes from the fact that controllers are becoming like services, they house some logic in terms of what happens when and often they merge data from the response of two services. From my understanding the controller should be more of a pass-through mechanism.

What is the best approach to solve the above issue?

2 Answers 2

1

You're doing too much in the controller. A controller should only be doing HTTP-ish things, like validating and converting json into actual objects, or checking a CSRF token, or converting errors into HTTP status codes. Anything not related to the HTTP channel should be in services, where they are much easier to test (you won't have to mock an HTTP context), and where they can be reused by other channels.

If you have business operations that require more than one service, write a new service that uses both. Nobody said the services layer had to be a single layer.

class BlogPostCreatorService : IBlogPostCreatorService
{
    protected readonly IAccountService _accountService;
    protected readonly IBlogPostService _blogPostService;

    public BlogPostCreatorService(IAccountService accountService, IBlogPostService blogPostService)
    {
        _accountService = accountService;
        _blogPostService = blogPostService;
    }

    public SomeDto MyMethodThatUsesTwoServices(string accountId, string text)
    {
        var account = _accountService.GetAccount(accountId);
        return _blogPostService.CreatePost(account.FullName, text);
    }
}
1
  • Thanks! Can you please clarify why you state > If you have business operations that require more than one service, write a new service that uses both. -- I can guess the reason, is to avoid circular deps?
    – dendog
    Dec 21, 2021 at 16:04
3

Your question is very broad. It encapsulates almost any possible backend architecture, which is much to broad to fully answer. If you're looking for a solution on an architectural level, you really need to find books or resources on the architecture you're interested in.

This answer only focuses on what you can do to fix the code as it is; without reworking the entire backend architecture from scratch.

However my question comes from the fact that controllers are becoming like services, they house some logic in terms of what happens when and often they merge data from the response of two services.

It is more common for this merging of different sources to happen on the business level (i.e. Service), not the web level (i.e. Controller).

Think about it this way: if your application is now both a web service and a WPF app, you will have a web project and a WPF project which both depend on the same business logic project.

But this merged logic should apply equally to the web app and the WPF app, since you want the behavior to be the same for both applications. Therefore, putting that merged logic in the web app makes no sense, as you'd have to copy/paste it to the WPF app, therefore violating DRY.

The better solution here is to make your business logic the melting pot, which exposes a clear interface that can be used by any consumer (web app, WPF app, ...) without needing to reinvent how the logic all fits together.

4
  • Thanks! Small follow up question, what happens when certain services have a circular inverse dependency on each other? I mean that I need to inject auth into account and account into auth for example.
    – dendog
    Dec 19, 2021 at 12:16
  • @dendog: 99 times out of 100, this is indicative of a bad design. I don't have all the details here but I would put good money on you needing to redesign this to be one-way.
    – Flater
    Dec 20, 2021 at 23:23
  • I really appreciate the help! Any references or more information you could point me in the direction of? My modules keep needing to cross boundaries, and I am spending lots of time scratching my head thinking should this be in moduleA or moduleB. When you say bad design do you mean the distinction between modules is not clear?
    – dendog
    Dec 21, 2021 at 11:14
  • @dendog similar situation here. did you find a clean way of making things modular? May 30, 2022 at 3:05

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