What is a Use Case and how to implement it?
Clean Architecture Series - Use Cases Layer
Continuing with the Clean Architecture Series, in this article I explain what is a UseCase and how we can implement one using typescript.
The use case layer acts as a mediator between the adapters layer and the domain layer. It receives inputs from the controllers (adapters layer), applies the necessary business logic and operations, and then communicates with the data layer (domain layer) to fetch or store data as needed.
For our use case class we will implement the "Inversion of Control (IoC)" principle which allows us to delegate the responsibility of handling dependencies and object creation to the external layers of the Clean Architecture.
\if you are not familiar with IoC, please refer first to my previous article call "[Inversion of Control and Dependency Injection in typescript](maxmartinez.dev/inversion-of-control-and-de..)"*
First, go to the "use-case" folder and create a new folder named "policy" with a "create-policy.use-case.ts" file. After that, add the following code:
import { IPolicyRepository } from "@/domain/policy/policy-repository.interface";
import { PolicyEntity, Unmarshalled } from "@/domain/policy/policy.entity";
export class CreatePolicyUseCase {
constructor(private policyRepository: IPolicyRepository) {}
async execute(data: any): Promise<Unmarshalled> {
try {
const policy = await this.createPolicy(data);
return policy;
} catch (error) {
throw error;
}
}
private async createPolicy(data: any): Promise<Unmarshalled> {
const policyEntity = new PolicyEntity(data);
await this.policyRepository.save(policyEntity);
return policyEntity.unmarshalled();
}
}
Let's understand the CreatePolicyUseCase:
The constructor method, rather than directly creating an instance of PolicyRepository within “CreatePolicyUseCase”, we define an interface IPolicyRepository for the database repository and use Constructor Dependency Injection to manage the dependencies.
The execute method, is about error handling and processing exceptions. However, in this case we are calling the "createPolicy" method and throwing the exception to the higher level.
The createPolicy method, is about the processes of creating the policy record into the database. It has the logic to map the data from the request to the domain, create the entity and interact with the repository.
Using the IoC the outer layers are responsible for creating the PolicyRepository instance and injecting it into “CreatePolicyUseCase” in order to:
Define the dependency as a contract where only matters the behavior specified by the contract rather than the implementation details
Decouple the use case class from the specific implementation of the repository, making it more maintainable and testable.
The main goal of the use case layer is to encapsulate the business logic, making it independent of the presentation and data sources.
All done, I hope that this article was useful. See you in the next one.