Skip to content

Commit

Permalink
refactor: changed badrap/result for oxide.ts
Browse files Browse the repository at this point in the history
  • Loading branch information
Sairyss committed Apr 12, 2022
1 parent 64387c4 commit 547af4f
Show file tree
Hide file tree
Showing 12 changed files with 44 additions and 28 deletions.
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
**Check out my other repositories**:

- [Backend best practices](https://github.com/Sairyss/backend-best-practices) - Best practices, tools and guidelines for backend development.
- [Full-stack application example](https://github.com/Sairyss/full-stack-application-example) - an example of a simple full-stack application using NodeJS + NestJS, MongoDB + Mongoose, NX Monorepo, etc.
- [Distributed systems topics](https://github.com/Sairyss/backend-best-practices) - list of topics and resources related to distributed systems, system design, microservices, scalability and performance, etc

---

Expand Down Expand Up @@ -724,7 +724,8 @@ const user = await this.userRepo.create(user);
return Result.ok(user);
```

[@badrap/result](https://www.npmjs.com/package/@badrap/result) - this is a nice npm package if you want to use a Result object.
- [oxide.ts](https://www.npmjs.com/package/oxide.ts) - this is a nice npm package if you want to use a Result object
- [@badrap/result](https://www.npmjs.com/package/@badrap/result) - alternative

Returning errors instead of throwing them adds some extra boilerplate code, but can make your application more robust and secure.

Expand Down
11 changes: 11 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
"nest-event": "^1.0.8",
"nestjs-console": "^7.0.0",
"npm": "^7.24.1",
"oxide.ts": "^0.9.12",
"pg": "^8.5.1",
"reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2",
Expand Down
2 changes: 1 addition & 1 deletion src/libs/ddd/domain/base-classes/command-handler.base.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Result } from 'oxide.ts/dist';
import { UnitOfWorkPort } from '../ports/unit-of-work.port';
import { Result } from '../utils/result.util';
import { Command } from './command.base';

export abstract class CommandHandlerBase<
Expand Down
6 changes: 3 additions & 3 deletions src/libs/ddd/domain/base-classes/query-handler.base.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { Result } from '../utils/result.util';
import { Result } from 'oxide.ts/dist';

export abstract class Query {}

export abstract class QueryHandlerBase {
// For consistency with a CommandHandlerBase and DomainEventHandler
abstract handle(query: Query): Promise<Result<unknown>>;
abstract handle(query: Query): Promise<Result<unknown, Error>>;

execute(query: Query): Promise<Result<unknown>> {
execute(query: Query): Promise<Result<unknown, Error>> {
return this.handle(query);
}
}
1 change: 0 additions & 1 deletion src/libs/ddd/domain/utils/result.util.ts

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { UnitOfWorkPort } from '@src/libs/ddd/domain/ports/unit-of-work.port';
import { EntityTarget, getConnection, QueryRunner, Repository } from 'typeorm';
import { IsolationLevel } from 'typeorm/driver/types/IsolationLevel';
import { Logger } from 'src/libs/ddd/domain/ports/logger.port';
import { Result } from '@src/libs/ddd/domain/utils/result.util';
import { Err, Result } from 'oxide.ts/dist';

/**
* Keep in mind that this is a naive implementation
Expand Down Expand Up @@ -59,13 +59,13 @@ export class TypeormUnitOfWork implements UnitOfWorkPort {
this.logger.debug(`[Starting transaction]`);
await queryRunner.startTransaction(options?.isolationLevel);
// const queryRunner = this.getQueryRunner(correlationId);
let result: T | Result<T>;
let result: T | Result<T, Error>;
try {
result = await callback();
if (((result as unknown) as Result<T>)?.isErr) {
if (((result as unknown) as Result<T, Error>)?.isErr()) {
await this.rollbackTransaction<T>(
correlationId,
((result as unknown) as Result.Err<T, Error>).error,
((result as unknown) as Err<Error, T>).unwrapErr(),
);
return result;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import { IdResponse } from '@libs/ddd/interface-adapters/dtos/id.response.dto';
import { routesV1 } from '@config/app.routes';
import { ApiOperation, ApiResponse } from '@nestjs/swagger';
import { CommandBus } from '@nestjs/cqrs';
import { Result } from '@src/libs/ddd/domain/utils/result.util';
import { ID } from '@src/libs/ddd/domain/value-objects/id.value-object';
import { ConflictException } from '@src/libs/exceptions';
import { match, Result } from 'oxide.ts/dist';
import { CreateUserCommand } from './create-user.command';
import { CreateUserHttpRequest } from './create-user.request.dto';
import { UserAlreadyExistsError } from '../../errors/user.errors';
Expand Down Expand Up @@ -35,14 +35,16 @@ export class CreateUserHttpController {
UserAlreadyExistsError
> = await this.commandBus.execute(command);

return result.unwrap(
id => new IdResponse(id.value), // if ok return an id
error => {
// if error decide what to do with it
// Deciding what to do with a Result (similar to Rust matching)
// if Ok we return a response with an id
// if Error decide what to do with it depending on its type
return match(result, {
Ok: id => new IdResponse(id.value),
Err: error => {
if (error instanceof UserAlreadyExistsError)
throw new ConflictException(error.message);
throw error;
},
);
});
}
}
6 changes: 3 additions & 3 deletions src/modules/user/commands/create-user/create-user.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import { UserRepositoryPort } from '@modules/user/database/user.repository.port'
import { Address } from '@modules/user/domain/value-objects/address.value-object';
import { Email } from '@modules/user/domain/value-objects/email.value-object';
import { UnitOfWork } from '@src/infrastructure/database/unit-of-work/unit-of-work';
import { Result } from '@libs/ddd/domain/utils/result.util';
import { CommandHandler } from '@nestjs/cqrs';
import { CommandHandlerBase } from '@src/libs/ddd/domain/base-classes/command-handler.base';
import { Err, Ok, Result } from 'oxide.ts/dist';
import { CreateUserCommand } from './create-user.command';
import { UserEntity } from '../../domain/entities/user.entity';
import { UserAlreadyExistsError } from '../../errors/user.errors';
Expand All @@ -29,7 +29,7 @@ export class CreateUserService extends CommandHandlerBase {
if (await userRepo.exists(command.email)) {
/** Returning an Error instead of throwing it
* so a controller can handle it explicitly */
return Result.err(new UserAlreadyExistsError());
return Err(new UserAlreadyExistsError());
}

const user = UserEntity.create({
Expand All @@ -44,6 +44,6 @@ export class CreateUserService extends CommandHandlerBase {
user.someBusinessLogic();

const created = await userRepo.save(user);
return Result.ok(created.id);
return Ok(created.id);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { Body, Controller, Get, HttpStatus } from '@nestjs/common';
import { routesV1 } from '@config/app.routes';
import { UserHttpResponse } from '@modules/user/dtos/user.response.dto';
import { QueryBus } from '@nestjs/cqrs';
import { Result } from '@src/libs/ddd/domain/utils/result.util';
import { ApiOperation, ApiResponse } from '@nestjs/swagger';
import { Result } from 'oxide.ts/dist';
import { FindUsersQuery } from './find-users.query';
import { FindUsersHttpRequest } from './find-users.request.dto';
import { UserEntity } from '../../domain/entities/user.entity';
Expand All @@ -22,7 +22,9 @@ export class FindUsersHttpController {
@Body() request: FindUsersHttpRequest,
): Promise<UserHttpResponse[]> {
const query = new FindUsersQuery(request);
const result: Result<UserEntity[]> = await this.queryBys.execute(query);
const result: Result<UserEntity[], Error> = await this.queryBys.execute(
query,
);

/* Returning Response classes which are responsible
for whitelisting data that is sent to the user */
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { UserRepository } from '@modules/user/database/user.repository';
import { QueryHandlerBase } from '@src/libs/ddd/domain/base-classes/query-handler.base';
import { QueryHandler } from '@nestjs/cqrs';
import { Result } from '@libs/ddd/domain/utils/result.util';
import { Ok, Result } from 'oxide.ts/dist';
import { FindUsersQuery } from './find-users.query';
import { UserEntity } from '../../domain/entities/user.entity';

Expand All @@ -15,8 +15,8 @@ export class FindUsersQueryHandler extends QueryHandlerBase {
logic involved, it bypasses application's core completely
and retrieves users directly from a repository.
*/
async handle(query: FindUsersQuery): Promise<Result<UserEntity[]>> {
async handle(query: FindUsersQuery): Promise<Result<UserEntity[], Error>> {
const users = await this.userRepo.findUsers(query);
return Result.ok(users);
return Ok(users);
}
}
6 changes: 3 additions & 3 deletions src/modules/wallet/domain/entities/wallet.entity.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ArgumentOutOfRangeException } from '@libs/exceptions';
import { AggregateRoot } from '@libs/ddd/domain/base-classes/aggregate-root.base';
import { UUID } from '@libs/ddd/domain/value-objects/uuid.value-object';
import { Result } from '@src/libs/ddd/domain/utils/result.util';
import { Err, Ok, Result } from 'oxide.ts/dist';
import { WalletNotEnoughBalanceError } from '../../errors/wallet.errors';

export interface CreateWalletProps {
Expand Down Expand Up @@ -29,10 +29,10 @@ export class WalletEntity extends AggregateRoot<WalletProps> {

withdraw(amount: number): Result<null, WalletNotEnoughBalanceError> {
if (this.props.balance - amount < 0) {
return Result.err(new WalletNotEnoughBalanceError());
return Err(new WalletNotEnoughBalanceError());
}
this.props.balance -= amount;
return Result.ok(null);
return Ok(null);
}

/**
Expand Down

0 comments on commit 547af4f

Please sign in to comment.