question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Why usecase controller is extending an infrastructure class?

See original GitHub issue

App layer shouldn’t be aware of infrastructure implementation, why we extend an infrastructure implementation class?

I thought that the usecase controller shouldn’t know about if the usecase is implemented through websocket, HTTP, TCP/IP or Message Broker; this makes the BaseController coupled to the HTTP protocol (VERBS).

I think the idea would be to return always a Result<ControllerResponse>, develop a infrastructure implementation like HTTPRequestHandler (which requires a UseCase’s Controller) with HTTP verbs as methods, and then reuse it by Express, Koa… even at the same runtime or not. (progressive library migration, performance testing…)

We could create another TCPRequestController, MQRequestController, which all of them requires the same as HTTPRequestHandler.

export interface IRequestHandler<RQ> {
    req: RQ;

    handle<RQ>(req: RQ, controller: any): void;
    handle<RQ, RS>(req: RQ, controller: any, res: RS): void;
}
export interface IHttpRequestHandler<RQ, RS> extends IRequestHandler<RQ> {
  res: RS;
}
export abstract class HttpRequestHandler<RQ, RS> implements IHttpRequestHandler<RQ, RS>/*, IHttpRequestHandlerResponses*/ {
  req: any;
  res: any;
  controller: any;

  public handle<RQ, RS>(req: RQ, controller: any, res?: RS): void {
    this.req = req;
    this.res = res;
    this.controller = controller;

    this.controller.call();
  };

  abstract jsonResponse(): any;
}

HttpRequestHandler could have all HTTP verbs implemented or just leave the responsability for the ending library implementation:

export interface IExpressRouteHandler extends IHttpRequestHandler<express.Request, express.Response> {}

export abstract class ExpressRouteHandler extends HttpRequestHandler<express.Request, express.Response> {
  req: express.Request;
  res: express.Response;

  public static jsonResponse(res: express.Response, code: number, message: string) {
    return res.status(code).json({ message });
  }

  public ok(res: express.Response, dto?: string) {
    if(!!dto) {
      return res.status(200).json(dto);
    } else {
      return res.sendStatus(200);
    }
  }

  public created<T>(res: express.Response) {
    return res.sendStatus(201);
  }

  public clientError(message?: string) {
    return ExpressRouteHandler.jsonResponse(this.res, 400, message ? message : 'Unauthorized');
  }

  public unauthorized(message?: string) {
    return ExpressRouteHandler.jsonResponse(this.res, 401, message ? message : 'Unauthorized');
  }

  public paymentRequired(message?: string) {
    return ExpressRouteHandler.jsonResponse(this.res, 402, message ? message : 'Payment required');
  }

  public forbidden(message?: string) {
    return ExpressRouteHandler.jsonResponse(this.res, 403, message ? message : 'Forbidden');
  }

  public notFound(message?: string) {
    return ExpressRouteHandler.jsonResponse(this.res, 404, message ? message : 'Not found');
  }

  public conflict(message?: string) {
    return ExpressRouteHandler.jsonResponse(this.res, 409, message ? message : 'Conflict');
  }

  public tooMany(message?: string) {
    return ExpressRouteHandler.jsonResponse(this.res, 429, message ? message : 'Too many requests');
  }

  public todo() {
    return ExpressRouteHandler.jsonResponse(this.res, 400, 'TODO');
  }

  public fail(error: Error | string) {
    console.log(error);
    return this.res.status(500).json({
      message: error.toString()
    });
  }
}

I tried to deal with this but I’ve found a concern: What kind of response should the usecase controller return?

Here’s my alpha repository https://github.com/imsergiobernal/efecto-kettlebell

Issue Analytics

  • State:open
  • Created 4 years ago
  • Comments:7 (4 by maintainers)

github_iconTop GitHub Comments

4reactions
stemmlerjscommented, Oct 19, 2019

why the Controller is inside Usecase’s folder and not inside infra folder?

There’s an architectural principle called Package By Component.

xh9i

Notice how easy it is to determine exactly what the features of this app are? That’s called package by component/module. It’s when we put everything related to a component (the use cases are our components) into a single module. When we use the name of the use case as the folder name the app is practically telling us what it can do, without needing to look deep inside the files and classes.

That phenomenon is called Screaming Architecture. The app is screaming at us, letting the reader know exactly what problems it solves and what the domain is, with the actors and use cases. The alternative to this is *Package By Infrastructure.

why the Controller is inside Usecase’s folder and not inside infra application folder

Package by infrastructure is when you organize code according to the type of construct it is (controllers, application layer, infra, etc). When we do that, it hurts our ability to quickly identify where features are, and features get split across folders and files.

I really punched down on Uncle Bob’s approach to designing applications by centering them around the use cases. That means that every time I start a new project, I’ll identify the actors, the use cases and which subdomain those actors and use cases belong to.

When we package by component we get +1 readability, +1 cohesion (cohesive units of code).

Let’s look a little bit deeper at the createPost/ folder for example.

xh9k

Here’s really what I mean. In this cohesive component, we have everything we need in order to support the CreatePost.ts use case.

We have a:

  • controller (to hook it up to infrastructure, express.js web server)
  • dto (to specify the inputs to the use case)
  • errors namespace (to define all the ways that the use case can succeed or fail)

And let’s say for some reason we wanted to hook this use case up to a serverless function.

Depending on the service, we might be able to just export the controller and use that, but we might also need to define another adapter in order for the use case to be able to be used by the external infrastructure concern.

My point is: everything is centered around the use cases of the application.

It makes sense to co-locate things that are used together and their entire purpose of being is to work with each other (controller, dto, error namespace, use case).

This is the very essence of the Single Responsibility Principle.

Could you re-use the controller with another framework? Could you re-use the controller with another request interface (Infrastructure Event for instance)?

I don’t think you’d want to do that. What’s being exported from the module is the express controller and the use case itself.

import { DownvoteComment } from "./DownvoteComment";
import { postRepo, memberRepo, commentRepo, commentVotesRepo } from "../../../repos";
import { postService } from "../../../domain/services";
import { DownvoteCommentController } from "./DownvoteCommentController";

const downvoteComment = new DownvoteComment(
  postRepo, memberRepo, commentRepo, commentVotesRepo, postService
);

const downvoteCommentController = new DownvoteCommentController(
  downvoteComment
)

// we export the express controller and the use case itself
export {
  downvoteComment,                  
  downvoteCommentController 
}

The use case is what has reusability because it’s an application layer concern. The express controller does not. It has a specific purpose: to be hooked up to an express RESTful API.

If you for some reason needed to support another delivery mechanism, like GraphQL or SOAP, you could dependency inject the DownvoteComment use case into a GraphQLController or a SoapController (each other specific infrastructure layer concerns).

2reactions
imsergiobernalcommented, Nov 11, 2019

@stemmlerjs

Clean Architecture book, Use Case concept, page 188

Notice also that the use case does not describe the user interface other than to informally specify the data coming in from that interface, and the data going back out through that interface. From the use case, it is impossible to tell whether the application is delivered on the web, or on a thick client, or on a console, or is a pure service. This is very important. Use cases do not describe how the system appears to the user. Instead, they describe the application-specific rules that govern the interaction between the users and the Entities. How the data gets in and out of the system is irrelevant to the use cases.

I throw the following question. Can I reuse this use-case to a Console output without duplicating?

Read more comments on GitHub >

github_iconTop Results From Across the Web

Better Software Design with Application Layer Use Cases
In this article, we explore how organizing application logic as Use Cases in the application layer helps to make large typescript projects ......
Read more >
Controller in Clean Architecture - Stack Overflow
But controller in some frameworks has to extends a base class (for example, an AbstractController class), It also needs to receive an Request...
Read more >
Use-Case Controller
The Use-Case Controller pattern deals with the problem of mapping use case specifications to an implementation that is cost-effective.
Read more >
Clean Architecture - What is the difference between Use ...
Following the Clean Architecture approach, each use case should be a different class, implementing only one Execute method, which is the ...
Read more >
Domain layer | Android Developers
You should encapsulate repeatable business logic present in the UI layer in a use case class. This makes it easier to apply any...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found