Mr.UseCase
The perfect way to wrap your business logic fast and properly.
Introduction
These use cases orchestrate the flow of data to and from the entities, and direct those entities to use their Critical Business Rules to achieve the goals of the use case.
The UseCase layer allows you to achieve significant benefits in the following parts of writing code:
- Make development process clear for all participants
- Speed up the development of production-ready projects
- Avoid complexity
- Reduce coupling
So, developers and use cases have to be friends
Installation
Just one step.
npm i mr-use-case
And use it where you need it.
import { MrUseCase } from 'mr-use-case';
Localization
As well you have an option to localize errors through error builder customization:
import { MrUseCase } from 'mr-use-case';
import { ErrorsBuilder } from './errors.builder';
export function UseCase<T, R>() {
return MrUseCase<T, R>({ errorsBuilder: ErrorsBuilder });
}
Read more about it on
Overview
This section contains a simple use case that shows us an example of Mr.UseCase
implementation. Let's take a quick look at the following piece of code:
import { MrUseCase } from 'mr-use-case';
import { User } from '$path';
import { isEmail } from '$path';
interface Request {
email: string;
}
interface Response {
user: User;
}
export class UserCreateCase extends MrUseCase<Request, Response>() {
private position?: UserPosition;
private positionValidated: UserPosition;
// process
async process() {
await this.assignVariables();
await this.validate(); // calls checks()
const user = await User.create({
email: this.request.email,
positionId: this.positionValidated.id,
});
return { user };
}
// private
protected async checks() {
if (!this.request.email) {
this.errors.add('email', 'presence');
}
if (!isEmail(this.request.email)) {
this.errors.add('email', 'format');
}
if (!this.position) {
this.errors.add('email', 'positionFind');
return;
}
this.positionValidated = this.position;
}
private async assignVariables() {
this.position = await findAvailablePosition();
}
private async findAvailablePosition() {
// ...
}
}
As you can see, the code is split into four parts:
- Assigning a variable area
- Validation
- Execution
- Packing response
Keep in mind that one use case must fullfil only one business purpose and give a straight answer about the success of the operation after the call.
Now let's see how we may use it in the positive scenario:
const email = 'example@example.com';
const { user } = await UserCreateCase.call({ email });
return user; // => created user
However, what happens if the passed value is not an email? Let's change our code and see.
const email = 'wrongemail';
try {
const { user } = await UserCreateCase.call({ email });
} catch (err) {
if (err instanceof MrError) {
debug(err.errors.messages());
return;
}
throw err;
}
In this scenario our use case throws an exception containing all errors that were caught by the validation process. You may read more about the errors format at
Let's make a conclusion.
⚠️ At this moment you probably would like to see an integration of the module to something more ready to use. And specifically for this purpose🐨 Mr.Koa boilerplate exists.
Conclusion
The use cases make your work much simpler, more structured and efficient. And Mr.UseCase
pleasantly provides an interface to enjoy these advantages with no headache.
Give it a try!