1

Currently I don't understand the Use case concept enough good - too much abstract definitions but no particular examples. The section Use Cases of Chapter 20 Business Rules does not including any code or at least UML example.

The current question is discussing around below paragraph:

A use case is an object. It has one or more functions that implement the application-specific business rules. It also has data elements that include the input data, the output data, and the references to the appropriate Entities with which it interacts.

"Clean Architecture"

This raises a lot of questions, but according SO rules, I must to keep the topics focused on one question. I'll begin from: "Must the use cases extend the entities"?

Let us clarify the terminology first

enter image description here

  • The business rules are consists from Enterprise (Critical) business rules and Application business rules.
  • The only one type of Enterprise (Critical) business rules I know is the Entities. If nothing more exists, the Entity is also the synonym for Enterprise business rules and Critical business rules. In the languages supports OOP the Entities could and should be expressed by classes.
  • The Entities include only data which existing without computerization. Same about actions expressed by the methods.

Entity is pure business and nothing more.

  • The application business rules are containing the data which is not actual without computerization.

These rules would not be used in a manual environment, because the make sense only as part of an automated system.

  • I was though that Use cases in a synonym of application business rules. This answer let me know that I was wrong - the Use cases also including the interfaces of gateways (but not their implementations). What causes concerns is the "Use cases" and "Requests and response models" are separate sections of Chapter 20 Business rules.

  • I have not see the examples of Use cases classes. Below code examples are my improvisations based on a bare theory.

The examples where extending could be useful

Limitations of the system

Very strictly speaking, the these rules would make or save the business money, irrespective of whether the were implemented on a computer. They would make or save money even if they were executed manually.

Below TypeScript class is fully obeys to above concept because it contains only data which existing without computerization.

class Product {

  public title: string;
  public price__dollars__withoutTax: number; // The "__XX" is the Clarification postfix

  public constructor(properties: Readonly<{ title: string; price__dollars__withoutTax: number; }>) {
    this.title = properties.title;
    this.price__dollars__withoutTax = properties.price__dollars__withoutTax;
  }

}

Assume that I asked the customer "What the minimal and maximal characters count in the title"? and the customer has answered: "I have not thought about this. Decide it yourself while no inconveniences in the system you are developing".

I have checked the statistics and knew that most titles are less than, for example, 200 characters. With some margin, let it will be 300.

class Product {

  public title: string;
  public price__dollars__withoutTax: number;

  public readonly TITLE_MINIMAL_CHARACTERS_COUNT: number = 2;
  public readonly TITLE_MAXIMAL_CHARACTERS_COUNT: number = 300;

  public constructor(properties: Readonly<{ title: string; price__dollars__withoutTax: number; }>) {
    this.title = properties.title;
    this.price__dollars__withoutTax = properties.price__dollars__withoutTax;
  }

}

I expressed the above limitations with appropriate class fields, but these limitations does not exist without computerization - the enterprise (critical) business rules concept has been violated. To fix these issue, I must move these limitations to Use case class:

class Product {

  public title: string;
  public price__dollars__withoutTax: number;

  public constructor(properties: Readonly<{ title: string; price__dollars__withoutTax: number; }>) {
    this.title = properties.title;
    this.price__dollars__withoutTax = properties.price__dollars__withoutTax;
  }

}


class ProductUseCase extends Product {

  public readonly TITLE_MINIMAL_CHARACTERS_COUNT: number = 2;
  public readonly TITLE_MAXIMAL_CHARACTERS_COUNT: number = 300;

  public constructor(properties: Readonly<{ title: string; price__dollars__withoutTax: number; }>) {
    super(properties);
  }

}

New fields required by the application

For some reasons, the identifiers are frequently required for entities in various applications. However, the ID is actual only for the application thus must appear in Use cases:

class Product {

  public title: string;
  public price__dollars__withoutTax: number; // The "__XX" is the Clarification postfix

  public constructor(properties: Readonly<{ title: string; price__dollars__withoutTax: number; }>) {
    this.title = properties.title;
    this.price__dollars__withoutTax = properties.price__dollars__withoutTax;
  }

}


class ProductUseCase extends Product {

  public readonly ID: number = ProductUseCase.generateID();

  private static counterForID_Generating: number = 0;

  public constructor(properties: Readonly<{ title: string; price__dollars__withoutTax: number; }>) {
    super(properties);
  }


  private static generateID(): number {
    ProductUseCase.counterForID_Generating++;
    return ProductUseCase.counterForID_Generating;
  }  

}

In this way, in most cases every entity from Critical business rules must be wrapped by Use case.

Analysis of example at figure 20.2 (Chapter 20, Use cases)

enter image description here

So, the "A use case is an object". It's hard to implement above use case with one function - it will be the class with one public and multiple private methods.

For this example, there is no need to extend LoanUseCase from Loan. The code draft will be like:

class Loan {

  public principle: Loan.Principles;
  public rate__percentage: number;
  public period__months: number;

  public constructor(constructorParameters: Loan.ContructorParameters) {
    this.principle = constructorParameters.principle;
    this.rate__percentage = constructorParameters.rate__percentage;
    this.period__months = constructorParameters.period__months;
  }


  public makePayment(amount__dollars: number): void {

  }

  public applyInterest(): void { /* ... */ }

  public chargeLetFee(): void { /* ... */  }

}

namespace Loan {

  export type ContructorParameters = Readonly<{
    principle: Loan.Principles;
    rate__percentage: number;
    period__months: number;
  }>;

  export enum Principles {
    standard = "STANDARD",
    forYoungFamilies = "FOR_YONG_FAMILIES"
  }

}


type CustomerRawData = {
  firstName: string;
  lastName: string;
  // ...
};


class LoanUseCase {

  public static issueLoanOfPrimaryCourseIfPossible(customerRawData: CustomerRawData): Loan | null {

    if (!LoanUseCase.isCustomerDataValid(customerRawData)) {
      return null;
    }


    if (!LoanUseCase.isCreditScoreEnough(customerRawData)) {
      return null;
    }


    // create new customer and save him to data store
    
    return new Loan({ 
      period__months: 12,
      principle: Loan.Principles.standard,
      rate__percentage: 15
     });
  }


  private static isCustomerDataValid(customerRawData: CustomerRawData): boolean {
    // validate name ...
    // validate address, birthdate, etc.
    return true;
  }

  private static isCreditScoreEnough(customerRawData: CustomerRawData): boolean {
    // ...
    return true;
  }

}
2
  • 2
    "This answer let me know that I was wrong - the Use cases also including the interfaces of gateways (but not their implementations)." - you shouldn't think of them as the interfaces of Gateways/Controllers/Presenters; instead, you should see them as interfaces required by the Use Cases, that Gateways/Controllers/Presenters then implement. E.g. you wouldn't have a generic Presenter interface implemented by every Presenter, but Use Case–specific Presenter interfaces. As such, they help express application business rules (part of the use Use Code is written in terms of them). Sep 25, 2022 at 10:48
  • 2
    This arrangement lets Use Cases make calls to things in the Interface Adapters layer without knowing what they actually are (without knowing their concrete types). This is achieved via dependency injection. Don't get confused by the fact that code in one layer also references/calls/implements things found in the next layer towards the center - that is always going to be the case. Sep 25, 2022 at 10:59

1 Answer 1

3

"Must the use cases extend the entities"?

The short answer is no, use cases should never extend entities. The extends keyword represents inheritance, which implies an "IS-A" relationship between classes from an OO perspective.

Inheritance defines tight coupling on a subclass to its superclass by fusing all data and behaviour in both the subclass and superclass together into the same instance when an object is created.

Having a Use Case class extend an Entity class results in defining critical business data alongside the use case together in the same object, meaning no separation between the two.

To emphasise another line from the same paragraph:

A use case is an object. It has one or more functions that implement the application-specific business rules. It also has data elements that include the input data, the output data, and the references to the appropriate Entities with which it interacts.

Inheritance goes against the guidelines in Clean Architecture which recommends a much looser relationship than inheritance, with use cases holding references to whichever entities it interacts with, and not for entity functions and data to become part of a use case object.

Your Answer

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

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