Skip to content

Commit

Permalink
feat: add application definition to modules fruit and harvest
Browse files Browse the repository at this point in the history
  • Loading branch information
ldiego73 committed Sep 1, 2024
1 parent 0be5a30 commit 25f780b
Show file tree
Hide file tree
Showing 36 changed files with 959 additions and 7 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { Result, ok, err } from "@common/result";
import { FruitRepository } from "@modules/fruit/domain/repositories";
import {
Command,
CommandHandler,
CommandHandlerException,
} from "@shared/application";

import { FruitAlreadyExistsException } from "../exceptions";
import { Fruit } from "../../domain/aggregates";
import { Variety } from "../../domain/entities";
import {
FruitInvalidException,
VarietyInvalidException,
} from "../../domain/exceptions";

type Response = Result<
| CommandHandlerException
| FruitAlreadyExistsException
| FruitInvalidException
| VarietyInvalidException,
void
>;

export interface AddFruitCommand extends Command {
name: string;
varieties: {
name: string;
}[];
}

export class AddFruitCommandHandler extends CommandHandler<
AddFruitCommand,
Response
> {
constructor(private readonly repository: FruitRepository) {
super();
}

async handle(command: AddFruitCommand): Promise<Response> {
try {
const existingFruit = await this.repository.findByName(command.name);

if (existingFruit !== null) {
return err(new FruitAlreadyExistsException("Fruit already exists"));
}

const fruitOrError = Fruit.create({
name: command.name,
varieties: [],
});

if (fruitOrError.isErr()) {
return err(fruitOrError.error);
}

const fruit = fruitOrError.value;

const varietiesOrErrors = command.varieties.map((variety) => {
return Variety.create({
name: variety.name,
fruitId: fruit.id,
});
});

if (varietiesOrErrors.some((result) => result.isErr())) {
return err(varietiesOrErrors.find((result) => result.isErr())!.error);
}

const varieties = varietiesOrErrors.map((result) => result.value);

fruit.varieties = varieties;

await this.repository.save(fruit);

fruit.created();
fruit.commit();

return ok();
} catch (error: any) {
return err(CommandHandlerException.create(error.message));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { Result, err, ok } from "@common/result";
import { VarietyRepository } from "@modules/fruit/domain/repositories";
import {
Command,
CommandHandler,
CommandHandlerException,
} from "@shared/application";
import { UniqueEntityId } from "@shared/domain";

import { VarietyAlreadyExistsException } from "../exceptions";
import { Variety } from "../../domain/entities";
import { VarietyInvalidException } from "../../domain/exceptions";

type Response = Result<
| VarietyInvalidException
| VarietyAlreadyExistsException
| CommandHandlerException,
void
>;

export interface AddVarietyCommand extends Command {
name: string;
fruitId: string;
}

export class AddVarietyCommandHandler extends CommandHandler<
AddVarietyCommand,
Response
> {
private constructor(private readonly repository: VarietyRepository) {
super();
}

async handle(command: AddVarietyCommand): Promise<Response> {
try {
const existingVarieties = await this.repository.findAllByFruitId(
command.fruitId,
);

const existingVariety = existingVarieties.find(
(v) => v.fruitId.toValue() === command.fruitId,
);

if (typeof existingVariety !== "undefined") {
return err(
new VarietydAlreadyExistsException("Variety already exists"),
);
}

const varietyOrError = Variety.create({
name: command.name,
fruitId: new UniqueEntityId(command.fruitId),
});

if (varietyOrError.isErr()) {
return err(varietyOrError.error);
}

const variety = varietyOrError.value;

await this.repository.save(variety);

return ok();
} catch (error: any) {
return err(CommandHandlerException.create(error.message));
}
}
}
4 changes: 4 additions & 0 deletions back-end/src/modules/fruit/application/commands/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export * from "./add-variety.handler";
export * from "./add-fruit.handler";
export * from "./update-variety.handler";
export * from "./update-fruit.handler";
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { Result, ok, err } from "@common/result";
import { FruitRepository } from "@modules/fruit/domain/repositories";
import {
Command,
CommandHandler,
CommandHandlerException,
} from "@shared/application";

import { FruitAlreadyExistsException } from "../exceptions";
import { Fruit } from "../../domain/aggregates";
import { Variety } from "../../domain/entities";
import {
FruitInvalidException,
VarietyInvalidException,
} from "../../domain/exceptions";

type Response = Result<
| CommandHandlerException
| FruitAlreadyExistsException
| FruitInvalidException
| VarietyInvalidException,
void
>;

export interface UpdateFruitCommand extends Command {
id: string;
name: string;
varieties: {
name: string;
}[];
}
}

export class UpdateFruitCommandHandler extends CommandHandler<
UpdateFruitCommand,
Response
> {
constructor(private readonly repository: FruitRepository) {
super();
}

async handle(command: UpdateFruitCommand): Promise<Response> {
try {
const existingFruit = await this.repository.findByName(command.name);

if (existingFruit !== null && existingFruit.name !== command.name) {
return err(new FruitAlreadyExistsException("Fruit already exists"));
}

const fruitOrError = Fruit.create({
name: command.name,
varieties: [],
});

if (fruitOrError.isErr()) {
return err(fruitOrError.error);
}

const fruit = fruitOrError.value;

const varietiesOrErrors = command.varieties.map((variety) => {
return Variety.create({
name: variety.name,
fruitId: fruit.id,
});
});

if (varietiesOrErrors.some((result) => result.isErr())) {
return err(varietiesOrErrors.find((result) => result.isErr())!.error);
}

const varieties = varietiesOrErrors.map((result) => result.value);

fruit.varieties = varieties;

await this.repository.update(fruit, command.id);

fruit.updated();
fruit.commit();

return ok();
} catch (error: any) {
return err(CommandHandlerException.create(error.message));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { Result, err, ok } from "@common/result";
import { VarietyRepository } from "@modules/fruit/domain/repositories";
import {
Command,
CommandHandler,
CommandHandlerException,
} from "@shared/application";
import { UniqueEntityId } from "@shared/domain";

import {
VarietyAlreadyExistsException,
VarietyNotFoundException,
} from "../exceptions";
import { Variety } from "../../domain/entities";
import { VarietyInvalidException } from "../../domain/exceptions";

type Response = Result<
| VarietyInvalidException
| VarietyAlreadyExistsException
| VarietyNotFoundException
| CommandHandlerException,
void
>;

export interface UpdateVarietyCommand extends Command {
id: string;
name: string;
fruitId: string;
}

export class UpdateVarietyCommandHandler extends CommandHandler<
UpdateVarietyCommand,
Response
> {
private constructor(private readonly repository: VarietyRepository) {
super();
}

async handle(command: UpdateVarietyCommand): Promise<Response> {
try {
const foundVariety = await this.repository.findById(command.id);

if (foundVariety === null) {
return err(new VarietyNotFoundException("Variety not found"));
}

const existingVarieties = await this.repository.findAllByFruitId(
command.fruitId,
);

const existingVariety = existingVarieties.find(
(v) => v.fruitId.toValue() === command.fruitId,
);

if (
typeof existingVariety !== "undefined" &&
existingVariety.name !== command.name
) {
return err(new VarietyAlreadyExistsException("Variety already exists"));
}

const varietyOrError = Variety.create({
name: command.name,
fruitId: new UniqueEntityId(command.fruitId),
});

if (varietyOrError.isErr()) {
return err(varietyOrError.error);
}

const variety = varietyOrError.value;

await this.repository.update(variety, command.id);

return ok();
} catch (error: any) {
return err(CommandHandlerException.create(error.message));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { EventHandler } from "@shared/application";

import { FruitCreatedEvent } from "@modules/fruit/domain/events";

export class FruitCreatedEventHandler extends EventHandler<FruitCreatedEvent> {
async handle(event: FruitCreatedEvent): Promise<void> {
this.logger.info(`Fruit created: ${event.getId()}`);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { EventHandler } from "@shared/application";

import { FruitUpdatedEvent } from "@modules/fruit/domain/events";

export class FruitUpdatedEventHandler extends EventHandler<FruitUpdatedEvent> {
async handle(event: FruitUpdatedEvent): Promise<void> {
this.logger.info(`Fruit updated: ${event.getId()}`);
}
}
2 changes: 2 additions & 0 deletions back-end/src/modules/fruit/application/events/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from "./fruit-created.handler";
export * from "./fruit-updated.handler";
25 changes: 25 additions & 0 deletions back-end/src/modules/fruit/application/exceptions/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { Exception } from "@common/exception";

export class FruitAlreadyExistsException extends Exception {
constructor(message: string) {
super("FRUIT_ALREADY_EXISTS", message);
}
}

export class FruitNotFoundException extends Exception {
constructor(message: string) {
super("FRUIT_NOT_FOUND", message);
}
}

export class VarietyAlreadyExistsException extends Exception {
constructor(message: string) {
super("VARIETY_ALREADY_EXISTS", message);
}
}

export class VarietyNotFoundException extends Exception {
constructor(message: string) {
super("VARIETY_NOT_FOUND", message);
}
}
Loading

0 comments on commit 25f780b

Please sign in to comment.