diff --git a/hosts/web/index.html b/hosts/web/index.html
index 348d159..7d055c0 100644
--- a/hosts/web/index.html
+++ b/hosts/web/index.html
@@ -11,7 +11,7 @@
name="viewport"
content="width=device-width, initial-scale=1.0"
/>
-
Vite + React + TS
+ Web
diff --git a/modules/catalog/src/adapters/QuoteEntityGateway.ts b/modules/catalog/src/adapters/QuoteEntityGateway.ts
index e8c3d5f..44f8e45 100644
--- a/modules/catalog/src/adapters/QuoteEntityGateway.ts
+++ b/modules/catalog/src/adapters/QuoteEntityGateway.ts
@@ -1,9 +1,7 @@
import { success } from "@clean-architecture/shared-kernel";
-import type { IdValueObject } from "@clean-architecture/shared-kernel";
import type { QuoteEntityGatewayPort } from "../entities/QuoteEntityGatewayPort";
import { QuoteEntity } from "../entities/QuoteEntity";
-import { AuthorValueObject } from "../entities/AuthorValueObject";
export class QuoteEntityGateway implements QuoteEntityGatewayPort {
public async getMany() {
@@ -12,17 +10,17 @@ export class QuoteEntityGateway implements QuoteEntityGatewayPort {
return success([]);
}
- public async getOne(id: IdValueObject) {
+ public async getOne(id: string) {
await Promise.resolve();
- // TODO: refacto to be internalized inside the QuoteEntity (to prevent anemic model)
- const author = AuthorValueObject.create({
- firstName: "test",
- lastName: "test",
- });
-
- if (author.type === "failure") return author;
+ const fullName = "Test Test";
+ const [firstName, lastName] = fullName.split(" ") as [string, string];
- return QuoteEntity.create(id, author.payload, "Fake content");
+ return QuoteEntity.create({
+ id,
+ content: "Fake content",
+ firstName,
+ lastName,
+ });
}
}
diff --git a/modules/catalog/src/entities/QuoteEntity.ts b/modules/catalog/src/entities/QuoteEntity.ts
index 360442d..e1912c6 100644
--- a/modules/catalog/src/entities/QuoteEntity.ts
+++ b/modules/catalog/src/entities/QuoteEntity.ts
@@ -1,30 +1,52 @@
-import { Entity, Guard, success } from "@clean-architecture/shared-kernel";
-import type { IdValueObject } from "@clean-architecture/shared-kernel";
+import {
+ Entity,
+ Guard,
+ IdValueObject,
+ success,
+} from "@clean-architecture/shared-kernel";
+import type { GetValueFromValueObject } from "@clean-architecture/shared-kernel";
import { CreatedAtValueObject } from "./CreatedAtValueObject";
-import type { AuthorValueObject } from "./AuthorValueObject";
-
-export class QuoteEntity extends Entity {
- public createdAt: CreatedAtValueObject;
-
- private constructor(
- public override id: IdValueObject,
- public author: AuthorValueObject,
- public content: string,
- ) {
- super(id);
- this.createdAt = CreatedAtValueObject.create();
+import { AuthorValueObject } from "./AuthorValueObject";
+
+type QuoteEntityAttributes = {
+ id: IdValueObject;
+ author: AuthorValueObject;
+ content: string;
+ createdAt: CreatedAtValueObject;
+};
+
+type QuoteEntityCreateInput = GetValueFromValueObject &
+ Pick & {
+ id: string;
+ };
+
+export class QuoteEntity extends Entity {
+ private constructor(public override attributes: QuoteEntityAttributes) {
+ super(attributes);
}
- public static override create(
- id: IdValueObject,
- author: AuthorValueObject,
- content: string,
- ) {
- const guardResult = Guard.mustBeLessThanCharacters(content, 280);
+ public static override create({
+ id,
+ content,
+ firstName,
+ lastName,
+ }: QuoteEntityCreateInput) {
+ const guardContentResult = Guard.mustBeLessThanCharacters(content, 280);
+
+ if (guardContentResult.type === "failure") return guardContentResult;
+
+ const author = AuthorValueObject.create({ firstName, lastName });
- if (guardResult.type === "failure") return guardResult;
+ if (author.type === "failure") return author;
- return success(new QuoteEntity(id, author, content));
+ return success(
+ new QuoteEntity({
+ id: IdValueObject.create(id),
+ author: author.payload,
+ content,
+ createdAt: CreatedAtValueObject.create(),
+ }),
+ );
}
}
diff --git a/modules/catalog/src/useCases/GetQuoteUseCase.ts b/modules/catalog/src/useCases/GetQuoteUseCase.ts
index 8150958..52908a4 100644
--- a/modules/catalog/src/useCases/GetQuoteUseCase.ts
+++ b/modules/catalog/src/useCases/GetQuoteUseCase.ts
@@ -1,8 +1,4 @@
-import {
- IdValueObject,
- UseCaseInteractor,
- success,
-} from "@clean-architecture/shared-kernel";
+import { UseCaseInteractor, success } from "@clean-architecture/shared-kernel";
import type {
RequestModel,
ResponseModel,
@@ -24,14 +20,17 @@ export class GetQuoteUseCase extends UseCaseInteractor<
QuoteEntityGatewayPort
> {
public override async execute(requestModel: GetQuoteRequestModel) {
- const id = IdValueObject.create(requestModel.id);
- const entityGatewayResult = await this.entityGateway.getOne(id);
+ const entityGatewayResult = await this.entityGateway.getOne(
+ requestModel.id,
+ );
if (entityGatewayResult.type === "failure") {
this.presenter.error(entityGatewayResult);
} else {
this.presenter.ok(
- success({ content: entityGatewayResult.payload.content }),
+ success({
+ content: entityGatewayResult.payload.attributes.content,
+ }),
);
}
}
diff --git a/modules/shared-kernel/src/Entity.ts b/modules/shared-kernel/src/Entity.ts
index 384d426..5aa44ae 100644
--- a/modules/shared-kernel/src/Entity.ts
+++ b/modules/shared-kernel/src/Entity.ts
@@ -1,27 +1,37 @@
import type { Result } from "@open-vanilla/result";
-import { IdValueObject } from "./IdValueObject";
+import type { GetValueFromValueObject } from "./ValueObject";
+import type { IdValueObject } from "./IdValueObject";
import { Guard } from "./Guard";
import type { DomainObject } from "./DomainObject";
-export abstract class Entity implements DomainObject {
- protected constructor(
- public id: IdValueObject = IdValueObject.create(crypto.randomUUID()),
- ) {}
+type EntityAttributes = { id: IdValueObject };
- public static create(..._: unknown[]): Entity | Result {
+export abstract class Entity<
+ Attributes extends EntityAttributes = EntityAttributes,
+> implements DomainObject
+{
+ protected constructor(public attributes: Attributes) {}
+
+ public static create(_input: {
+ id: GetValueFromValueObject;
+ }): Entity | Result {
throw new Error("NotImplementedException");
}
+ public static isInstanceOf(input: unknown): input is Entity {
+ return input instanceof Entity;
+ }
+
public equals(input: unknown) {
if (this === input) return true;
if (
- !(input instanceof Entity) ||
+ !Entity.isInstanceOf(input) ||
Guard.mustBeDefinedAndNonNull(input).type === "failure"
)
return false;
- return this.id.equals(input.id);
+ return this.attributes.id.equals(input.attributes.id);
}
}
diff --git a/modules/shared-kernel/src/EntityGateway.ts b/modules/shared-kernel/src/EntityGateway.ts
index 88d0c63..882e565 100644
--- a/modules/shared-kernel/src/EntityGateway.ts
+++ b/modules/shared-kernel/src/EntityGateway.ts
@@ -1,6 +1,7 @@
import type { Result } from "@open-vanilla/result";
import type { AnyRecord } from "./types";
+import type { GetValueFromValueObject } from "./ValueObject";
import type { Entity } from "./Entity";
export type EntityGateway<
@@ -8,5 +9,7 @@ export type EntityGateway<
Methods = AnyRecord,
> = Methods & {
getMany: () => Promise>;
- getOne: (id: E["id"]) => Promise>;
+ getOne: (
+ id: GetValueFromValueObject,
+ ) => Promise>;
};
diff --git a/modules/shared-kernel/src/IdValueObject.ts b/modules/shared-kernel/src/IdValueObject.ts
index 8c63014..1ccde2c 100644
--- a/modules/shared-kernel/src/IdValueObject.ts
+++ b/modules/shared-kernel/src/IdValueObject.ts
@@ -10,7 +10,7 @@ type Value = string;
* if they are both strings but impossible if they are represented through a dedicated type)).
*/
export class IdValueObject extends ValueObject {
- public static override create(input: Value) {
+ public static override create(input: Value = crypto.randomUUID()) {
return new IdValueObject(input);
}
}
diff --git a/modules/shared-kernel/src/ValueObject.ts b/modules/shared-kernel/src/ValueObject.ts
index d7c3982..c9330c5 100644
--- a/modules/shared-kernel/src/ValueObject.ts
+++ b/modules/shared-kernel/src/ValueObject.ts
@@ -24,7 +24,7 @@ export abstract class ValueObject implements DomainObject {
public readonly value: Value;
public static create(
- ..._: unknown[]
+ _input: GetValueFromValueObject>,
): Result> | ValueObject {
throw new Error("NotImplementedException");
}
@@ -42,3 +42,6 @@ export abstract class ValueObject implements DomainObject {
return JSON.stringify(this) === JSON.stringify(input);
}
}
+
+export type GetValueFromValueObject> =
+ Input["value"];
diff --git a/modules/shared-kernel/src/index.ts b/modules/shared-kernel/src/index.ts
index 0f143a7..3e4143b 100644
--- a/modules/shared-kernel/src/index.ts
+++ b/modules/shared-kernel/src/index.ts
@@ -5,6 +5,7 @@ export { Guard } from "./Guard";
export { IdValueObject } from "./IdValueObject";
export { Presenter } from "./Presenter";
export { UseCaseInteractor } from "./UseCase";
+export type { GetValueFromValueObject } from "./ValueObject";
export { ValueObject } from "./ValueObject";
export type { RequestModel } from "./RequestModel";
export type { ResponseModel } from "./ResponseModel";